JUC高并发编程
四、线程间定制化通信
4.1)线程间定制化通信案例
A线程打印2次AA,B线程打印4次BB,C线程打印6次CC,按照此顺序循环3轮
4.2)线程间定制化通信案例代码实现
4.2.1)Lock接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
4.2.1.1)lock()
lock()方法是平常使用得最多的一个方法,就是用来获取锁。
如果锁已被其他线程获取,则进行等待;采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。
因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
通常使用Lock 来进行同步的话,是以下面这种形式去使用的:
Lock lock = new ReentrantLock();
//上锁
lock.lock();
try{
//处理任务
}catch(Exception ex){
...
}finally{
//释放锁
lock.unlock();
}
4.2.1.2)newCondition
关键字synchronized与wait()/notify()这两个方法一起使用可以实现等待/通知模式,Lock锁的newContition()方法返回Condition对象,Condition类也可以实现等待/通知模式。
用notify()通知时,JVM会随机唤醒某个等待的线程,使用Condition类可以进行选择性通知,Condition比较常用的两个方法:
-
await()会使当前线程等待,同时会释放锁,当其他线程调用signal()时,线程会重 新获得锁并继续执行;
-
signal()用于唤醒一个等待的线程。
注意:在调用Condition的await()/signal()方法前,也需要线程持有相关的Lock锁,调用await()后线程会释放这个锁,在singal()调用后会从当前 Condition对象的等待队列中,唤醒一个线程,唤醒的线程尝试获得锁,一旦获得锁成功就继续执行。
4.2.2)代码实现
流程分析如下图:
代码如下:
package com.study.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//第一步 创建资源类
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();
//打印2次,参数第几轮
public void print2(int loop) throws InterruptedException {
//上锁
lock.lock();
try {
//判断:flag值是否是1,如果不是1,等待
while (flag != 1) {
//等待
c1.await();
}
//干活:如果flag值是1,就输出信息
for (int i = 1; i <= 2; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + " :: " + i + " :轮数:" + loop);
}
//通知:通知BB线程
//修改标志位为 2 -> BB
flag = 2;
//通知BB线程
c2.signal();
} finally {
//释放锁
lock.unlock();
}
}
//打印4次,参数第几轮
public void print4(int loop) throws InterruptedException {
lock.lock();
try {
//判断:flag值是否是2,如果不是2,等待
while (flag != 2) {
c2.await();
}
//干活:如果flag值是2,就输出信息
for (int i = 1; i <= 4; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + " :: " + i + " :轮数:" + loop);
}
//通知:通知CC线程
//修改标志位为 3 -> CC
flag = 3;
//通知CC线程
c3.signal();
} finally {
lock.unlock();
}
}
//打印6次,参数第几轮
public void print6(int loop) throws InterruptedException {
lock.lock();
try {
//判断:flag值是否是3,如果不是3,等待
while (flag != 3) {
c3.await();
}
//干活:如果flag值是3,就输出信息
for (int i = 1; i <= 6; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + " :: " + i + " :轮数:" + loop);
}
//通知:通知AA线程
//修改标志位为 1 -> AA
flag = 1;
//通知AA线程
c1.signal();
} finally {
lock.unlock();
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
// 创建线程 AA
new Thread(() -> {
// 循环调用 3 次
for (int i = 1; i <= 3; i++) {
try {
shareResource.print2(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AA").start();
// 创建线程 BB
new Thread(() -> {
// 循环调用 3 次
for (int i = 1; i <= 3; i++) {
try {
shareResource.print4(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BB").start();
// 创建线程 CC
new Thread(() -> {
// 循环调用 3 次
for (int i = 1; i <= 3; i++) {
try {
shareResource.print6(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "CC").start();
}
}
输出:
com.study.lock.ThreadDemo3
当前线程名:AA :: 1 :轮数:1
当前线程名:AA :: 2 :轮数:1
当前线程名:BB :: 1 :轮数:1
当前线程名:BB :: 2 :轮数:1
当前线程名:BB :: 3 :轮数:1
当前线程名:BB :: 4 :轮数:1
当前线程名:CC :: 1 :轮数:1
当前线程名:CC :: 2 :轮数:1
当前线程名:CC :: 3 :轮数:1
当前线程名:CC :: 4 :轮数:1
当前线程名:CC :: 5 :轮数:1
当前线程名:CC :: 6 :轮数:1
当前线程名:AA :: 1 :轮数:2
当前线程名:AA :: 2 :轮数:2
当前线程名:BB :: 1 :轮数:2
当前线程名:BB :: 2 :轮数:2
当前线程名:BB :: 3 :轮数:2
当前线程名:BB :: 4 :轮数:2
当前线程名:CC :: 1 :轮数:2
当前线程名:CC :: 2 :轮数:2
当前线程名:CC :: 3 :轮数:2
当前线程名:CC :: 4 :轮数:2
当前线程名:CC :: 5 :轮数:2
当前线程名:CC :: 6 :轮数:2
当前线程名:AA :: 1 :轮数:3
当前线程名:AA :: 2 :轮数:3
当前线程名:BB :: 1 :轮数:3
当前线程名:BB :: 2 :轮数:3
当前线程名:BB :: 3 :轮数:3
当前线程名:BB :: 4 :轮数:3
当前线程名:CC :: 1 :轮数:3
当前线程名:CC :: 2 :轮数:3
当前线程名:CC :: 3 :轮数:3
当前线程名:CC :: 4 :轮数:3
当前线程名:CC :: 5 :轮数:3
当前线程名:CC :: 6 :轮数:3