【Java】线程的同步机制
1.线程安全问题
三个售票窗口,总票数为100张,卖票过程中,线程a在操作ticket,未操作完成时,其他线程参与进来,也操作ticket。导致出现重票、错票问题。(线程安全问题)。
class TicketWindow implements Runnable{ private int ticket = 100; //100张票 @Override public void run() { while (true){ if (ticket>0){ System.out.println(Thread.currentThread().getName()+":卖票,票号为:" + ticket); ticket--; }else { break; } } } } class WindowTest{ public static void main(String[] args) { TicketWindow w = new TicketWindow(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
2.解决
当线程a操作ticket时,其他线程不能参与进来,直到线程a操作完成。
在Java中,通过同步机制来解决线程安全问题。
方式一:同步代码块,synchronized修饰代码块
(1)共享数据:在这里就是ticket,被多个线程共同操作
(2)同步监视器(锁),任何一个类的对象(多个线程必须共用一把锁)
① 实现Runnable接口的方式可以使用this,因为只有一个对象
Window1 w = new Window1();
。
② 继承Thread类的方式可以使用当前类synchronized (Window2.class)
,类也是一个对象,只会加载一次;使用this则会代表t1,t2,t3多个对象。
class Window1 implements Runnable { private int ticket = 100; @Override public void run() { while (true) { synchronized (this) { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":售票,票号" + ticket); ticket--; } else { break; } } } } } public class WindowTest1 { public static void main(String[] args) { Window1 w = new Window1(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
点击查看代码:继承Thread类的方式,ticket要用static修饰
class Window2 extends Thread { private static int ticket = 100; @Override public void run() { while (true) { synchronized (Window2.class) { if (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":售票,票号"+ticket); ticket--; }else { break; } } } } } public class WindowTest2 { public static void main(String[] args) { Window2 t1 = new Window2(); Window2 t2 = new Window2(); Window2 t3 = new Window2(); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
方式二:使用同步方法,synchronized修饰方法
(1)同步方法中的同步监视器不需要显式声明
(2)非静态同步方法的同步监视器是this
(3)静态同步方法的同步监视器是当前类本身,因此继承Thread类的方式,同步方法要加static修饰,写为private static synchronized void show()
。因为用不了this。
class Window3 implements Runnable { private int ticket = 100; private boolean flag = true; @Override public void run() { while (flag) { show(); } } private synchronized void show() { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":售票,票号" + ticket); ticket--; }else { flag = false; } } } public class WindowTest3 { public static void main(String[] args) { Window3 w = new Window3(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
方式三:使用Lock锁,JDK5.0新增
创建对象ReentrantLock lock = new ReentrantLock();
手动上锁:lock.lock();
手动解锁:lock.unlock();
class Window implements Runnable { private int ticket = 100; private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); //上锁,开启同步 if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":售票,票号" + ticket); ticket--; } else { break; } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); //解锁,关闭同步 } } } } public class LockTest { public static void main(String[] args) { Window w = new Window(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
synchronized与lock的异同
- 相同:二者都可以解决线程安全问题
- 不同:
- synchronized在执行完同步代码后,自动释放同步监视器
- Lock需要手动启动同步(lock()),结束同步也需要手动关闭同步(unlock())
优先使用顺序
① LOCK
② 同步代码块(已经进入了方法体,分配了相应资源)
③ 同步方法(在方法体之外)
分类:
Java / JAVA高级
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本