JUC并发编程共享模型之管程(三)(下)
创始人
2024-06-03 00:52:07
0

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) 的区别

  1. sleep 是Thread 方法,而wait 是Object的方法
  2. sleep 不需要强制和synchronized 配合使用,但 wait 需要和 synchronized 一起用
  3. sleep 在睡眠的同时,不会释放对象锁的, 但 wait 在等待时候会释放对象锁
  4. 它们状态 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 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) response).size());}private static List download() throws InterruptedException {// 模拟下载List 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) 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) 如果提前被唤醒,这时已经经历的时间假设为 400timePass = 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 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 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 response = (List) 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,仅会补充一份备用干粮

在这里插入图片描述

  1. 当前线程调用Unsafe.park()方法
  2. 检查 _counter ,本情况值为0, 获得 _mutex 互斥锁
  3. 线程进入 _cond 条件变量阻塞
  4. 设置 _counter = 0

在这里插入图片描述

  1. 调用Unsafe.unpark(Thread_0) 方法,设置 _counter为1
  2. 唤醒 _cond条件变量中的Thread-0
  3. Thread-0 恢复运行
  4. 设置_counter 为0

在这里插入图片描述

  1. 调用Unsafe.unpark(Thread-0),设置_counter 为1
  2. 当前线程调用 Unsafe.park() 方法
  3. 检查 _counter,本情况为1,这时线程无需阻塞,继续运行
  4. 设置_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
@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
  • 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
  • RUNNABLE <–> TIMED_WAITING

    • 当前线程调用 t.join(long n)方法, 当前线程从 RUNNABLE --> TIMED_WAITING
      • 注意是当前线程t线程对象的监视器上等待
    • 当前线程等待时间超过了n毫秒,或t线程运行结束,或调用了当前线程的 interrupt() ,当前线程从 TIMED_WAITING --> RUNNABLE
  • 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();}
}

在这里插入图片描述

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...