一、线程间定制化通信
多个线程之间的执行时机并不是固定的,是由 CPU 来操作调度的,如果要让线程按照我们需要的顺序执行,这就是线程间定制化通信。
二、案例
1、题目要求:
三个线程之间按顺序调用,实现 A -> B -> C
三个线程启动,要求如下:
AA打印5次,BB打印10次,CC打印15次
接着
AA 打印5次,BB打印10次,CC打印15次
……进行10轮
2、思考:
(1)有顺序通知,需要有标识位;
(2)有一个锁 Lock,3把钥匙 Condition;
(3)判断标识位;
(4)输出线程名 + 第几次 + 第几轮;
(5)修改标志位,通知下一个;
3、代码实现
//第一步:创建资源类
class ShareResource {
//定义标志位
// 1 AA 2 BB 3 CC
private int flag = 1;
//创建Lock锁
private Lock lock = new ReentrantLock();
//创建三个 Condition
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
//打印5次,参数第几轮
public void print5(int loop) throws InterruptedException {
//上锁
lock.lock();
try {
//第二步:判断 干活 通知
//判断:防止虚假唤醒,使用 while 循环
while (flag != 1) {
c1.await();
}
//干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
}
//通知
flag = 2; //修改标志位 2
c2.signal(); //通知 BB 线程,唤醒 c2 等待的线程
} finally {
//解锁
lock.unlock();
}
}
//打印10次,参数第几轮
public void print10(int loop) throws InterruptedException {
//上锁
lock.lock();
try {
//第二步:判断 干活 通知
//判断:防止虚假唤醒,使用 while 循环
while (flag != 2) {
c2.await();
}
//干活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
}
//通知
flag = 3; //修改标志位 3
c3.signal(); //通知 CC 线程,唤醒 c3 等待的线程
} finally {
//解锁
lock.unlock();
}
}
//打印15次,参数第几轮
public void print15(int loop) throws InterruptedException {
//上锁
lock.lock();
try {
//第二步:判断 干活 通知
//判断:防止虚假唤醒,使用 while 循环
while (flag != 3) {
c3.await();
}
//干活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
}
//通知
flag = 1; //修改标志位 1
c1.signal(); //通知 AA 线程,唤醒 c1 等待的线程
} finally {
//解锁
lock.unlock();
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
//创建多个线程,调用资源类的操作方法
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AA").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print10(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BB").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "CC").start();
}
}
三、多线程编程步骤
第一步:创建资源类,在资源类创建数据和操作方法;
第二步:在资源类操作方法
(1)判断
(2)干活
(3)通知
第三步:创建多个线程,调用资源类的操作方法;
第四步:注意标志位的修改和定位;