一、等待与唤醒
/** * 线程通讯问题 * Object wait, notify, notifyAll * Condition await signal signAll * CountDownLatch 当前线程等待若干个其他线程执行完成之后再执行 * CyclicBarrier 一组线程等待某个状态之后再全部开始执行 * Semaphore 控制某一组资源的访问权限 */
- 代码案例,奇数线程打印奇数,偶数线程打印偶数
1、使用Object自带的方法实现等待与唤醒:
/** * 休眠唤醒案例 * 打印10以内的奇偶数 * 奇数线程打印,偶数线程等待 * * 这个案例使用锁对象实现 */ static class OddAndEvenDemo { private int printNo = 0; private Object lockObj = new Object(); /** * 奇数打印方法,由奇数线程调用 */ public void odd() { while (printNo < 10) { synchronized (lockObj) { if (!isEvenNo(printNo)) { System.out.println("奇数:" + printNo); printNo ++; lockObj.notify(); } else { try { lockObj.wait(); // 等待偶数线程执行完毕 } catch (Exception exception) { exception.printStackTrace(); } } } } } /** * 偶数打印方法,偶数线程调用 */ public void even() { while (printNo < 10) { synchronized (lockObj) { if (isEvenNo(printNo)) { System.out.println("偶数:" + printNo); printNo ++; lockObj.notify(); } else { try { lockObj.wait(); // 等待偶数线程执行完毕 } catch (Exception exception) { exception.printStackTrace(); } } } } } }
判断是否奇偶数的方法:
static boolean isEvenNo(int evenNo) { return evenNo % 2 == 0; }
执行部分:
/** * 使用锁对象自身的等待与唤醒方法实现 */ @Test public void useLockObj() { OddAndEvenDemo oddAndEvenDemo = new OddAndEvenDemo(); // 开启奇数线程 Thread oddThread = new Thread(() -> oddAndEvenDemo.odd()); // 开启偶数线程 Thread evenThread = new Thread(() -> oddAndEvenDemo.even()); oddThread.start(); evenThread.start(); }
2、使用Condition实现等待与唤醒:
/** * 等待唤醒Condition方法 */ static class OddAndEvenDemo2 { private int printNo = 0; private Lock lock = new ReentrantLock(); // 不设置为公平锁 private Condition condition = lock.newCondition(); /** * 奇数打印方法,由奇数线程调用 */ public void odd() { while (printNo < 10) { lock.lock(); try { if (!isEvenNo(printNo)) { System.out.println("奇数:" + printNo); printNo ++; condition.signal(); } else { try { condition.await(); } catch (Exception exception) { exception.printStackTrace(); } } } catch (Exception exception) { exception.printStackTrace(); } finally { lock.unlock(); } } } /** * 偶数打印方法,偶数线程调用 */ public void even() { while (printNo < 10) { lock.lock(); try { if (isEvenNo(printNo)) { System.out.println("偶数:" + printNo); printNo ++; condition.signal(); } else { try { condition.await(); // 等待偶数线程执行完毕 } catch (Exception exception) { exception.printStackTrace(); } } } catch (Exception exception) { exception.printStackTrace(); } } } }
执行部分:
/** * 使用Condition对象方法实现 */ @Test public void useCondition() { OddAndEvenDemo2 oddAndEvenDemo = new OddAndEvenDemo2(); // 开启奇数线程 Thread oddThread = new Thread(() -> oddAndEvenDemo.odd()); // 开启偶数线程 Thread evenThread = new Thread(() -> oddAndEvenDemo.even()); oddThread.start(); evenThread.start(); }
- Object方法和Condition的区别总结:
1、Object锁对象基于同步关键字组合使用,等待与唤醒都是使用Object的wait & notify 且锁使用syncornized
2、Condition用于配合Lock对象组合使用,等待与唤醒使用 signal & await方法
二、指定数量等待 CountDownLatch
代码案例:
设置三个运动员线程和一个教练线程
只有等待三个运动员线程准备就绪之后,教练线程开始吹口哨开始训练
package cn.cloud9.test.multithread; import java.util.concurrent.CountDownLatch; /** * */ public class CountDownLatchDemo { static class CoachRacerDemo { private CountDownLatch cdl = new CountDownLatch(3); // 设置需要等待的线程数量 /** * 运动员方法 */ public void racer() { // 获取线程名称 String name = Thread.currentThread().getName(); System.out.println(name + " is preparing ... "); try { Thread.sleep(1000); } catch (Exception exception) { exception.printStackTrace(); } System.out.println(name + " is ready!"); cdl.countDown(); } /** * 教练方法 */ public void coach() { String name = Thread.currentThread().getName(); System.out.println(name + " wait racer prepare ready ..."); try { cdl.await(); } catch (Exception exception) { exception.printStackTrace(); } System.out.println("all racer is ready! start training!"); } } public static void main(String[] args) { CoachRacerDemo coachRacerDemo = new CoachRacerDemo(); Thread racer1 = new Thread(() -> coachRacerDemo.racer(), "racer-01"); Thread racer2 = new Thread(() -> coachRacerDemo.racer(), "racer-02"); Thread racer3 = new Thread(() -> coachRacerDemo.racer(), "racer-03"); Thread coach = new Thread(() -> coachRacerDemo.coach(), "coach"); // coach线程会先等待其他线程执行,直到等待数量的线程都执行完毕之后开始继续执行 coach.start(); racer1.start(); racer2.start(); racer3.start(); } }
三、统一执行 CyclicBarrier
package cn.cloud9.test.multithread; import java.util.Date; import java.util.concurrent.CyclicBarrier; /** * CyclicBarrier * 作用: * 让一组线程等待到某个状态之后,再全部同时执行 * CyclicBarrier底层基于 ReentrantLock和Condition实现 * */ public class CyclicBarrierDemo { static class RunTogetherDemo { final CyclicBarrier cyclicBarrier = new CyclicBarrier(3); // 参与同时起跑的线程数 public void startThread(int sec) { String name = Thread.currentThread().getName(); System.out.println(name + " 正在准备..."); try { Thread.sleep(sec); cyclicBarrier.await(); } catch (Exception exception) { exception.printStackTrace(); } System.out.println(name + " 已经启动完毕:" + new Date().getTime()); } } public static void main(String[] args) { final RunTogetherDemo cyclicBarrierDemo = new RunTogetherDemo(); Thread thread1 = new Thread(() -> cyclicBarrierDemo.startThread(300)); Thread thread2 = new Thread(() -> cyclicBarrierDemo.startThread(400)); Thread thread3 = new Thread(() -> cyclicBarrierDemo.startThread(500)); thread1.start(); thread2.start(); thread3.start(); } }
执行之后三个线程会在同一时刻开始执行await方法后的代码块
尽管之前让线程睡眠了不同时长,最后启动完毕的时间戳是一样的
四、资源访问控制 Semaphore
代码案例:
8个工人 使用 3台机器,机器为互斥资源(即每次只能让一个工人来操作)
package cn.cloud9.test.multithread; import java.util.concurrent.Semaphore; /** * 互斥案例 */ public class SemaphoreDemo { /** * */ static class WorkMachineDemo implements Runnable { private int worker; private Semaphore semaphore; public WorkMachineDemo(int worker, Semaphore semaphore) { this.worker = worker; this.semaphore = semaphore; } @Override public void run(){ try { // 1、工人获取机器 semaphore.acquire(); // 2、打印工人获取到机器,开始工作 String name = Thread.currentThread().getName(); System.out.println(name + " 获取到机器,开始作业"); // 3、线程睡眠一秒,模拟工人机器操作中 Thread.sleep(1000); // 3、使用完毕,工人下机 semaphore.release(); System.out.println(name + " 作业完毕,工人下机"); } catch (Exception exception) { exception.printStackTrace(); } } } public static void main(String[] args) { int worker = 8; Semaphore semaphore = new Semaphore(3); WorkMachineDemo workMachineDemo = new WorkMachineDemo(worker, semaphore); for (int i = 0; i < worker; i++) { new Thread(workMachineDemo).start(); } } }