JUC(二)线程间通信
线程间通信
多线程编程步骤
- 创建资源类,编写属性和操作方法
- 在资源中的操作方法
- 判断
- 干活
- 通知
- 创建多个线程,调用资源类的操作方法
- 使用while循环进行条件判断,防止虚假唤醒问题
一个加减实例 & 虚假唤醒问题
实现对一个初始值0进行轮流加减操作
public class AddAndSub { private int num; public int getNum() { return num; } public AddAndSub() { num = 0; } public synchronized void add() { // 判断 while(num != 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 业务逻辑 num++; System.out.println(Thread.currentThread().getName() + " + 1"); // 通知 notifyAll(); } public synchronized void sub() { while(num != 1) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } num--; System.out.println(Thread.currentThread().getName() + " - 1"); notifyAll(); } } class RunAddAndSub { public static void main(String[] args) { AddAndSub addAndSub = new AddAndSub(); Runnable r1 = () -> { for(int i = 0; i < 10; i++) { addAndSub.add(); } }; Runnable r2 = () -> { for(int i = 0; i < 10; i++) { addAndSub.sub(); } }; new Thread(r1, "A").start(); new Thread(r2, "B").start(); System.out.println(addAndSub.getNum()); } }
⭐为什么要在条件判断使用while?
这是因为比如add方法A线程+1notifyAll后,可能是处于等待状态的C线程抢到了锁,但是这时候num并不是0,所以要使用while在线程唤醒后再次判断。这种问题称为虚假唤醒问题
,
Lock接口实现
lock.newCondition设置等待条件
class Share { private int num = 0; private final Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void incr() { lock.lock(); while(num != 0) { try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } num++; System.out.println(Thread.currentThread().getName() + " + 1"); condition.signalAll(); lock.unlock(); } public void decr() { lock.lock(); while(num == 0) { try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } num--; System.out.println(Thread.currentThread().getName() + " - 1"); condition.signalAll(); lock.unlock(); } } public class TheadDemo { public static void main(String[] args) { var share = new Share(); Runnable r1 = () -> { for(int i = 0; i < 10; i++) { share.incr(); } }; Runnable r2 = () -> { for(int i = 0; i < 10; i++) { share.decr(); } };; new Thread(r1, "A").start(); new Thread(r2, "B").start(); new Thread(r1, "C").start(); new Thread(r2, "D").start(); } }
注意condition.signalAll()要写在unlock之前
线程间定制化通信
如上面的四个线程执行的时候,只是+-线程的顺序执行,执行相同方法的线程是没有顺序的,规定相同方法的线程的执行顺序,即是线程的定制化通信。
线程间通信案例
启动三个线程,要求如下:
- AA打印5次,BB打印10次,CC打印15次
- 如此进行10轮
设置标志位
当标志位为1,AA执行,当标志位为2,BB执行,当标志位为3,CC执行
class Print { private int flag = 1; public synchronized void print5(int num) throws InterruptedException { while (flag != 1) { this.wait(); } for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + ":::" + i + " ,loop" + num); } flag = 2; this.notifyAll(); } public synchronized void print10(int num) throws InterruptedException { while (flag != 2) { this.wait(); } for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + ":::" + i + " ,loop" + num); } flag = 3; this.notifyAll(); } public synchronized void print15(int num) throws InterruptedException { while (flag != 3) { this.wait(); } for (int i = 0; i < 15; i++) { System.out.println(Thread.currentThread().getName() + ":::" + i + " ,loop" + num); } flag = 1; this.notifyAll(); } } public class ThreadDemo2 { public static void main(String[] args) { Print print = new Print(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { try { print.print5(i); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { try { print.print10(i); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "BB").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { try { print.print15(i); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "CC").start(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?