con|

Tod4

园龄:2年11个月粉丝:21关注:0

JUC(二)线程间通信

线程间通信

多线程编程步骤

  1. 创建资源类,编写属性和操作方法
  2. 在资源中的操作方法
    1. 判断
    2. 干活
    3. 通知
  3. 创建多个线程,调用资源类的操作方法
  4. 使用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();
}
}
posted @   Tod4  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
   
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起