4.8 wait notify
根据前面很清楚了,关于为什么需要wait
- 因为如果一个线程被调用,但是不符合运行条件,需要让他将锁释放,继续去等待,等待他的运行条件满足
- 在不满足运行条件的情况下,我们不能给他重新放回EntryList中,让他继续去竞争,所以就有了WaitSet
- 如果他运行条件满足了,再让他进入EntryList,重新去竞争锁
原理
- Owner 线程发现条件不满足,调用wait 方法,即可进入WaitSet 变为WAITING状态
- BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用CPU 时间片
- BLOCKED 线程会在 Owner 线程释放时唤醒
- WAITING 线程会在Owner 线程调用notify 或 notifyAll 时唤醒,但唤醒后并不意味着立刻获得锁,仍需进入EntryList 重新竞争
API 介绍
- obj.wait() 让进入 object 监视器的线程到 waitSet 等待
- obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
- obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这几个方法
@Slf4j
public class WaitNotifyTest01 {
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized(obj){
log.debug("执行");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其他代码");
}
},"t1").start();
new Thread(() -> {
synchronized(obj){
log.debug("执行");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其他代码");
}
},"t2").start();
sleep(2);
log.debug("唤醒obj上其他线程");
synchronized (obj){
obj.notify(); // 唤醒obj上一个线程
//obj.notifyAll(); // 唤醒obj上所有等待线程
}
}
}
notify的结果
notifyAll的结果
- wait()方法会释放对象的锁,进入WaitSet等待区,从而让其他线程就有机会获取对象的锁。无限制的等待,直到notify()为止
- wait(long n) 有时限的等待,到n毫秒后结束等待,或是被notify
使用场景
sleep(long n) 和 wait(long n) 的区别
- sleep 是Thread 方法,而wait 是Object的方法
- sleep 不需要强制和synchronized 配合使用,但 wait 需要和 synchronized 一起用
- sleep 在睡眠的同时,不会释放对象锁的, 但 wait 在等待时候会释放对象锁
- 它们状态 TIMED_WAITING
synchronized(lock) {
while(条件不成立) {
lock.wait();
}
// 干活
}
//另一个线程
synchronized(lock) {
lock.notifyAll();
}
同步模式之保护性暂停
1.定义
即 Guarded Suspension,用在一个线程等待另一个线程的执行结果
要点:
- 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
- 如果有结果不断从一个线程到另一个线程那么可以使用消息队列
- JDK中,join的实现,Future的实现,采用的就是此模式
- 因为要等待另一方的结果,因此归类到同步模式
2.代码实现GuardedObject
@Slf4j
public class GuardObjectTest {
public static void main(String[] args) {
GuardObject guardObject = new GuardObject();
new Thread(() -> {
List<String> response = null;
try {
response = download();
log.debug("下载结束");
guardObject.compelete(response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
log.debug("waiting。。。。");
// 主线程阻塞等待
Object response = guardObject.get();
log.debug("获得 :[{}]",((List<String>) response).size());
}
private static List<String> download() throws InterruptedException {
// 模拟下载
List<String> list = new ArrayList<>();
Thread.sleep(2000);
for(int i = 0; i < 10; i++){
list.add(i+"下载完毕");
}
return list;
}
}
class GuardObject {
private Object response;
private final Object lock = new Object();
public Object get() {
synchronized (lock){
// 条件不满足则等待
while (response == null){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
public void compelete(Object response) {
synchronized (lock){
// 条件满足,通知等待线程
this.response = response;
lock.notifyAll();
}
}
}
3.带超时版本的GuardedObject
@Slf4j
public class GuardObjectTest02 {
public static void main(String[] args) {
GuardObject02 guardObject = new GuardObject02();
new Thread(() -> {
sleep(1);
guardObject.compelete(null);
sleep(1);
guardObject.compelete(Arrays.asList("a","b","c"));
}).start();
// 不超时
//Object response = guardObject.get(2500);
// 超时
Object response = guardObject.get(1500);
if (response != null){
log.debug("get response [{}]",((List<String>) response).size());
}else{
log.debug("no get response");
}
}
}
@Slf4j
class GuardObject02 {
private Object response;
private final Object lock = new Object();
public Object get(long millis) {
synchronized (lock){
// 记录开始时间
long begin = System.currentTimeMillis();
// 已经过去的时间
long timePass = 0;
// 条件不满足则等待
while (response == null){
// 假设等待1000ms ,结果在400 ms时行了, 那么还有600要等
long waitTime = millis - timePass;
log.debug("需要等待的时间:{}", waitTime);
if(waitTime <= 0){
log.debug("break...");
break;
}
try {
lock.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 3) 如果提前被唤醒,这时已经经历的时间假设为 400
timePass = System.currentTimeMillis() - begin;
log.debug("timePass:{}, Object is null {}",timePass,response == null);
}
return response;
}
}
public void compelete(Object response) {
synchronized (lock){
// 条件满足,通知等待线程
this.response = response;
lock.notifyAll();
}
}
}
异步模式之生产者消费者
1.定义
要点
- 与前面的保护性暂停中的GuardObject不同,不需要产生结果和消费结果的线程一一对应
- 消费队列可以用来平衡生产和消费的线程资源
- 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据
- 消息队列是有容器限制的,满时不会再加入数据,空时不会再消耗数据
- JDK 中各种阻塞队列,采用的就是这种模式
2.实现
class Message {
private int id;
private Object message;
public Message(int id, Object message) {
this.id = id;
this.message = message;
}
public int getId() {
return id;
}
public Object getMessage() {
return message;
}
}
class MessageQueue {
private LinkedList<Message> queue;
private int capacity;
public MessageQueue(int capacity) {
this.capacity = capacity;
queue = new LinkedList<>();
}
public Message take() {
synchronized (queue) {
while (queue.isEmpty()) {
log.debug("没货了, wait");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message message = queue.removeFirst();
queue.notifyAll();
return message;
}
}
public void put(Message message) {
synchronized (queue) {
while (queue.size() == capacity) {
log.debug("库存已达上限, wait");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.addLast(message);
queue.notifyAll();
}
}
}
应用
MessageQueue messageQueue = new MessageQueue(2);
// 4 个生产者线程, 下载任务
for (int i = 0; i < 4; i++) {
int id = i;
new Thread(() -> {
try {
log.debug("download...");
List<String> response = Downloader.download();
log.debug("try put message({})", id);
messageQueue.put(new Message(id, response));
} catch (IOException e) {
e.printStackTrace();
}
}, "生产者" + i).start();
}
// 1 个消费者线程, 处理结果
new Thread(() -> {
while (true) {
Message message = messageQueue.take();
List<String> response = (List<String>) message.getMessage();
log.debug("take message({}): [{}] lines", message.getId(), response.size());
}
}, "消费者").start();
4.9 park & unpark
基本使用
它们是 LockSupport 类中的方法
LockSupport.park() // 暂停当前线程
LockSupport.unpark() // 恢复某个线程的运行
先 park 再 unpark
@Slf4j
public class ParkTest01 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("start。。。");
sleep(1);
log.debug("park。。。");
LockSupport.park();
log.debug("resume....");
},"t1");
t1.start();
sleep(3);
log.debug("unpark");
LockSupport.unpark(t1);
}
}
先unpark 再 park
@Slf4j
public class ParkTest01 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("start。。。");
sleep(1);
log.debug("park。。。");
LockSupport.park();
log.debug("resume....");
},"t1");
t1.start();
sleep(0.5);
log.debug("unpark");
LockSupport.unpark(t1);
}
}
特点
与Object的 wait & notify 相比
- wait、notify和notifyAll 必须配合 Object Monitor 一起使用,而 park unpark 不必
- park & unpark 是以线程为单位来 【阻塞】和【唤醒】线程,而 notifyAll 是唤醒所有等待线程,就不那么 【精确】
- park & unpark 可以先 unpark,而 wait & notify 不能先 notify
原理
每个线程都有自己的一个Parker 对象, 由三部分组成 _counter, _cond 和 _mutex
- 线程就像一个旅人,Parker 是他随身携带的背包, 条件变量就好比背包中的帐篷, _counter 就好比背包中的备用干粮(0为耗尽, 1为充足)
- 调用park就是看需要不需要停下休息
- 如果备用干粮耗尽, 那么钻进帐篷休息
- 如果备用干粮重组,那么不许停留,继续前进
- 调用unpark,就好比补充干粮
- 如果这时线程还在帐篷,就唤醒让他继续前进,
- 如果线程还在运行,那么在下次调用park时,仅是消耗点备用干粮,不需要停下休息
- 因为背包空间有限,多次调用unpark,仅会补充一份备用干粮
- 当前线程调用Unsafe.park()方法
- 检查 _counter ,本情况值为0, 获得 _mutex 互斥锁
- 线程进入 _cond 条件变量阻塞
- 设置 _counter = 0
- 调用Unsafe.unpark(Thread_0) 方法,设置 _counter为1
- 唤醒 _cond条件变量中的Thread-0
- Thread-0 恢复运行
- 设置_counter 为0
- 调用Unsafe.unpark(Thread-0),设置_counter 为1
- 当前线程调用 Unsafe.park() 方法
- 检查 _counter,本情况为1,这时线程无需阻塞,继续运行
- 设置_counter 为0
4.10重新理解线程状态
假设有线程 t
- NEW --> RUNNABLE
- 调用 t.start()方法,由NEW --> RUNNABLE
- RUNNABLE <–> WAITING
- t 线程用 synchronized(obj) 获取了对象锁后
- 调用obj.wait() 方法,RUNNABLE --> WAITING
- 调用 obj.notify() ,obj.notifyAll(),t.interrupt() 时
- 竞争锁成功,t 线程从 WAITING --> RUNNABLE
- 竞争锁失败,t 线程从 WAITING --> BLOCKED
- t 线程用 synchronized(obj) 获取了对象锁后
@Slf4j
public class TestWaitNotify {
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj){
log.debug("执行");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其他代码"); // debug
}
},"t1").start();
new Thread(() -> {
synchronized (obj){
log.debug("执行");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其他代码"); // debug
}
},"t2").start();
sleep(0.5);
log.debug("唤醒obj上的其他线程");
synchronized (obj){
obj.notifyAll(); // 唤醒obj上所有等待线程 debug
}
}
}
-
RUNNABLE <–> WAITING
- 当前线程 调用 t.join() 方法,当前线程从RUNNABLE --> WAITING
- 注意是 当前线程 在 t线程对象的监视器上等待
- t线程运行结束后,或调用了 当前线程 的 interrupt()时 , 当前线程从 WAITING --> RUNNABLE
- 当前线程 调用 t.join() 方法,当前线程从RUNNABLE --> WAITING
-
RUNNABLE <–> WAITING
- 当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE --> WAITING
- 调用 LockSupport0.unpark(目标线程) 或调用了线程的 interrupt(),会让目标线程从 WAITING --> RUNNABLE
-
RUNNABLE <–> TIMED_WAITING
- t线程用 synchronized(obj) 获取了对象锁后
- 调用 obj.wait(long n) 方法,t线程从 RUNNABLE <–> TIME_WAITING
- t 线程等待时间超过了n毫秒, 或调用了 **obj.notify(),obj.notifyAll(),t.interrupt()**时
- 竞争锁成功, t线程 从 TIMED_WAITING --> RUNNABLE
- 竞争锁失败,t线程从 TIMED_WAITING --> BLOCKED
- t线程用 synchronized(obj) 获取了对象锁后
-
RUNNABLE <–> TIMED_WAITING
- 当前线程调用 t.join(long n)方法, 当前线程从 RUNNABLE --> TIMED_WAITING
- 注意是当前线程在t线程对象的监视器上等待
- 当前线程等待时间超过了n毫秒,或t线程运行结束,或调用了当前线程的 interrupt() ,当前线程从 TIMED_WAITING --> RUNNABLE
- 当前线程调用 t.join(long n)方法, 当前线程从 RUNNABLE --> TIMED_WAITING
-
RUNNABLE <–> TIMED_WAITING
- 当前线程调用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 时,当前线 程从 RUNNABLE --> TIMED_WAITING
- 调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,或是等待超时,会让目标线程从 TIMED_WAITING --> RUNNABLE
-
RUNNABLE --> BLOCKED
- t线程用 **synchronized(obj)**获取了对象锁时如果竞争失败,从 RUNNABLE --> BLOCKED
- 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有BLOCKED的线程重新竞争,如果其中 t线程竞争成功,从 BLOCKED --> RUNNABLE,其它失败的线程仍然 BLOCKED
-
RUNNABLE <–> TERMINATED
- 当前线程所有代码运行完毕,进入 TERMINATED
4.11 多把锁
多把不相干的锁
一间大屋子有两个功能:睡觉、学习,互不相干。 现在小南要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低 解决方法是准备多个房间(多个对象锁)
单个锁:
@Slf4j
class BigRoom {
public void sleep() {
synchronized (this) {
log.debug("sleeping 2 小时");
Sleeper.sleep(2);
}
}
public void study() {
synchronized (this) {
log.debug("study 1 小时");
Sleeper.sleep(1);
}
}
}
@Slf4j
public class ManySynchronizedTest01 {
public static void main(String[] args) {
BigRoom room = new BigRoom();
new Thread(() -> {
room.sleep();
Sleeper.sleep(2);
}).start();
new Thread(() -> {
room.study();
Sleeper.sleep(2);
}).start();
}
}
多个锁:
@Slf4j
class BigRoom {
private final Object sleepRoom = new Object();
private final Object studyRoom = new Object();
public void sleep() {
synchronized (sleepRoom) {
log.debug("sleeping 2 小时");
Sleeper.sleep(2);
}
}
public void study() {
synchronized (studyRoom) {
log.debug("study 1 小时");
Sleeper.sleep(1);
}
}
}
@Slf4j
public class ManySynchronizedTest01 {
public static void main(String[] args) {
BigRoom room = new BigRoom();
new Thread(() -> {
room.sleep();
Sleeper.sleep(2);
}).start();
new Thread(() -> {
room.study();
Sleeper.sleep(2);
}).start();
}
}
- 好处,可以增强并发度
- 坏处,如果一个线程需要同时获得多把锁,容易发生死锁
4.12 活跃性
死锁
- 一个线程需要同时获取多把锁,这时就容易发生死锁
@Slf4j
public class LockDeadTest {
public static void main(String[] args) {
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
synchronized (A) {
log.debug("lock A");
sleep(1);
synchronized (B) {
log.debug("lock B");
log.debug("操作...");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (B) {
log.debug("lock B");
sleep(0.5);
synchronized (A) {
log.debug("lock A");
log.debug("操作...");
}
}
}, "t2");
t1.start();
t2.start();
}
}
**发生了死锁 **
- jconsole 检测
-
jps 定位进程id,再用 jstack 定位死锁
-
避免死锁要注意加锁顺序
-
另外如果由于某个线程进入死循环,导致其他线程一直等待,对于这种情况linux可以通过top先定位到CPU占用高的Java进程,再利用 top -Hp 进程id 来定位是哪个线程,最后用jstack排查
活锁
活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束
类比死循环
饥饿
一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束
4.13 ReentrantLock
跟 synchronized 相比
- 可中断
- 可以设置超时间
- 可以设置为公平锁
- 可以支持多个条件变量
与 synchronized 一样,都支持可重入
基本语法
// 获取锁
reentrantLock.lock();
try {
// 临界区
} finally {
// 释放锁
reentrantLock.unlock();
}
可重入
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁 如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住
@Slf4j
public class ReentrantLockTest01 {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
public static void method1() {
lock.lock();
try {
log.debug("execute method1");
method2();
} finally {
lock.unlock();
}
}
public static void method2() {
lock.lock();
try {
log.debug("execute method2");
method3();
} finally {
lock.unlock();
}
}
public static void method3() {
lock.lock();
try {
log.debug("execute method3");
} finally {
lock.unlock();
}
}
}
可打断
@Slf4j
public class ReentrantLockTest02 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("启动...");
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("等锁的过程中被打断");
return;
}
try{
log.debug("获得了锁");
}finally {
lock.unlock();
}
},"t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
sleep(1);
t1.interrupt();
log.debug("执行打断");
} finally {
lock.unlock();
}
}
}
注意
- 如果是不可中断模式,那么即使使用了 interrupt 也不会让等待中断
@Slf4j
public class ReentrantLockTest03 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("启动...");
lock.lock();
try{
log.debug("获得了锁");
}finally {
lock.unlock();
}
},"t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
sleep(1);
t1.interrupt();
log.debug("执行打断");
} finally {
log.debug("释放了锁");
lock.unlock();
}
}
}
锁超时
立刻失败
@Slf4j
public class ReentrantLockTest03 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("启动...");
if (!lock.tryLock()){
log.debug("没获取到锁,直接结束");
return;
}
try {
log.debug("获得了锁");
}finally {
lock.unlock();
}
},"t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
sleep(2);
} finally {
lock.unlock();
}
}
}
超时失败
@Slf4j
public class ReentrantLockTest05 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("启动...");
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)){
log.debug("尝试获取锁等1s,没获取到直接返回");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.debug("获得了锁");
}finally {
lock.unlock();
}
},"t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
sleep(2);
} finally {
lock.unlock();
}
}
}
公平锁
ReentrantLock 默认是不公平的
@Slf4j
public class ReentrantLockTest06 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock(false);
lock.lock();
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "t" + i).start();
}
// 1s 之后去争抢锁
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " start...");
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "强行插入").start();
lock.unlock();
}
}
- 强行插入,有机会在中间输出
- 显示了不公平锁,有竞争
改为公平锁
ReentrantLock lock = new ReentrantLock(true);
- 强行插入,总是在最后输出
- 公平锁一般没有必要,会降低并发度
条件变量
- synchronized 中也有条件变量,也就是WaitSet,当条件不满足时进入 waitSet 等待
- ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的
- synchronized 是不满足条件的线程都在WaitSet 等待
- ReentrantLock 支持多个类似WaitSet的,对于WaitSet有更多的区分
使用要点
- await 前需要获得锁
- await 执行后,会释放锁,进入 conditionObject 等待 await 的线程被唤醒(或打断、或超时)去重新竞争 lock 锁
- 竞争 lock 锁成功后,从 await 后继续执行
@Slf4j
public class ReentrantLockTest07 {
static ReentrantLock lock = new ReentrantLock();
static Condition waitCigaretteQueue = lock.newCondition();
static Condition waitBreakfastQueue = lock.newCondition();
static volatile boolean hasCigarette = false;
static volatile boolean hasBreakfast = false;
public static void main(String[] args) {
new Thread(() -> {
lock.lock();
while(!hasCigarette){
try {
waitCigaretteQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
log.debug("等到了他要的smoke");
}finally {
lock.unlock();
}
}).start();
new Thread(() -> {
lock.lock();
while(!hasBreakfast){
try {
waitBreakfastQueue.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
log.debug("等到了他要的breakfast");
}finally {
lock.unlock();
}
}).start();
sleep(1);
sendBreakfast();
sleep(1);
sendCigarette();
}
private static void sendCigarette() {
lock.lock();
try {
log.debug("送烟来了");
hasCigarette = true;
waitCigaretteQueue.signal();
} finally {
lock.unlock();
}
}
private static void sendBreakfast() {
lock.lock();
try {
log.debug("送早餐来了");
hasBreakfast = true;
waitBreakfastQueue.signal();
} finally {
lock.unlock();
}
}
}
同步模式之顺序控制
1.固定运行顺序
- 先打印 t2 再打印 t1
wait notify
@Slf4j
public class SequentialControllerTest01 {
// 用来同步的对象
static Object obj = new Object();
// 判断t2是否运行过
static boolean t2runed = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (obj){
// 如果t2 没有执行过
while(!t2runed){
// t1 等会
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("t1");
});
Thread t2 = new Thread(() -> {
System.out.println("t2");
synchronized (obj){
// 修改运行标记
t2runed = true;
// 通知obj上等待的线程 (可能多个)
obj.notifyAll();
}
});
t1.start();
t2.start();
}
}
park unpark
@Slf4j
public class SequentialControllerTest02 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park();
System.out.println("t1");
});
Thread t2 = new Thread(() -> {
System.out.println("t2");
LockSupport.unpark(t1);
});
t1.start();
t2.start();
}
}
park 和 unpark 方法比较灵活,他俩谁先调用,谁后调用无所谓。并且是以线程为单位进行『暂停』和『恢复』, 不需要『同步对象』和『运行标记』
2.交替输出
线程1输出 a 5次,线程 2 输出 b 5次, 线程 3 输出 c 5次。现在要求输出 abcabcabcabcabc
wait notify
class SyncWaitNotify {
private int flag;
private int loopNumber;
public SyncWaitNotify(int flag, int loopNumber) {
this.flag = flag;
this.loopNumber = loopNumber;
}
public void print(int waitFlag, int nextFlag, String str) {
for (int i = 0; i < loopNumber; i++) {
synchronized (this) {
while (this.flag != waitFlag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(str);
flag = nextFlag;
this.notifyAll();
}
}
}
}
@Slf4j
public class SequentialControllerTest03 {
public static void main(String[] args) {
SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
new Thread(() -> {
syncWaitNotify.print(1, 2, "a");
}).start();
new Thread(() -> {
syncWaitNotify.print(2, 3, "b");
}).start();
new Thread(() -> {
syncWaitNotify.print(3, 1, "c");
}).start();
}
}
Lock 条件变量
@Slf4j
public class AwaitSignal extends ReentrantLock {
// 循环次数
private int loopNumber;
public AwaitSignal(int loopNumber) {
this.loopNumber = loopNumber;
}
public void start(Condition first){
this.lock();
try {
log.debug("start");
first.signal();
}finally {
this.unlock();
}
}
public void print(String str, Condition current, Condition next) {
for (int i = 0; i < loopNumber; i++) {
this.lock();
try {
current.await();
log.debug(str);
next.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
this.unlock();
}
}
}
public static void main(String[] args) {
AwaitSignal as = new AwaitSignal(5);
Condition aWaitSet = as.newCondition();
Condition bWaitSet = as.newCondition();
Condition cWaitSet = as.newCondition();
new Thread(() -> {
as.print("a",aWaitSet, bWaitSet);
}).start();
new Thread(() -> {
as.print("b", bWaitSet, cWaitSet);
}).start();
new Thread(() -> {
as.print("c", cWaitSet, aWaitSet);
}).start();
as.start(aWaitSet);
}
}
park unpark
@Slf4j
public class SyncPark {
private int loopNumber;
private Thread[] threads;
public SyncPark(int loopNumber) {
this.loopNumber = loopNumber;
}
public void setThreads(Thread... threads) {
this.threads = threads;
}
public void print(String str) {
for (int i = 0; i < loopNumber; i++) {
LockSupport.park();
System.out.println(str);
LockSupport.unpark(nextThread());
}
}
private Thread nextThread() {
Thread current = Thread.currentThread();
int index = 0;
for (int i = 0; i < threads.length; i++) {
if (threads[i] == current) {
index = i;
break;
}
}
if (index < threads.length - 1) {
return threads[index + 1];
} else {
return threads[0];
}
}
public void start() {
for (Thread thread : threads) {
thread.start();
}
LockSupport.unpark(threads[0]);
}
public static void main(String[] args) {
SyncPark syncPark = new SyncPark(5);
Thread t1 = new Thread(() -> {
syncPark.print("a");
});
Thread t2 = new Thread(() -> {
syncPark.print("b");
});
Thread t3 = new Thread(() -> {
syncPark.print("c");
});
syncPark.setThreads(t1,t2,t3);
syncPark.start();
}
}