JUC并发编程(四)常见模式
目录
一 同步与协调模式
1 保护性暂停模式
2 顺序控制模式
3 生产者消费者模式
4 Balking模式(犹豫模式)
二 线程管理/生命周期模式
1 两阶段终止模式
一 同步与协调模式
1 保护性暂停模式
一个线程需要等待另一个线程提供特定条件(通常是某个结果)满足后才能继续执行。
Guarded Suspension 保护性暂停是一种节点的多线程设计模式,用于在条件不满足时暂停线程执行,直到条件满足后在继续执行。(线程不满足等待条件,手动实现主动等待)
代码实现:
package day01.mysynchronized;import java.util.ArrayList;public class example6 {public static void main(String[] args) {GuardObject<ArrayList<Integer>> guardObject = new GuardObject<>();// 线程1等待线程2的结果new Thread(() -> {// 等待结果System.out.println("t1线程开始执行... 等待结果");ArrayList<Integer> result = guardObject.get();result.forEach(System.out::println);}, "t1").start();new Thread(() -> {System.out.println("t2线程开始执行...");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}ArrayList<Integer> objects = new ArrayList<>();objects.add(1);objects.add(2);guardObject.complete(objects);}, "t2").start();}
}class GuardObject<T> {private T response;public T get() {synchronized (this) {while (response == null) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}return response;}}public void complete(T response) {synchronized (this) {this.response = response;this.notifyAll();}}
}
更完善的:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;public class Example7 {public static void main(String[] args) {GuardObject<List<Integer>> guardObject = new GuardObject<>();// 线程1等待线程2的结果Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + "开始执行... 等待结果");try {// 设置5秒超时List<Integer> result = guardObject.get(5000);System.out.println(Thread.currentThread().getName() + "收到结果:");result.forEach(System.out::println);} catch (TimeoutException e) {System.out.println(Thread.currentThread().getName() + "等待结果超时");} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "被中断");}}, "t1");Thread t2 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + "开始执行...");try {// 模拟耗时操作Thread.sleep(3000);// 创建结果List<Integer> objects = new ArrayList<>();objects.add(1);objects.add(2);// 设置结果guardObject.complete(objects);System.out.println(Thread.currentThread().getName() + "已发送结果");} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "被中断");}}, "t2");t1.start();t2.start();// 确保主线程等待子线程完成try {t1.join();t2.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("所有线程执行完成");}
}/*** 增强版保护对象类* @param <T> 结果类型*/
class GuardObject<T> {private T response;private boolean done = false; // 完成状态标志/*** 获取结果(无限等待)* @return 结果对象* @throws InterruptedException 如果等待时被中断*/public T get() throws InterruptedException, TimeoutException {return get(0);}/*** 获取结果(带超时)* @param timeout 超时时间(毫秒),0表示无限等待* @return 结果对象* @throws InterruptedException 如果等待时被中断* @throws TimeoutException 如果超过指定的超时时间*/public T get(long timeout) throws InterruptedException, TimeoutException {synchronized (this) {long start = System.currentTimeMillis();long remaining = timeout;while (!done) {if (timeout > 0 && remaining <= 0) {throw new TimeoutException("等待结果超时");}if (timeout == 0) {this.wait();} else {this.wait(remaining);remaining = timeout - (System.currentTimeMillis() - start);}}return response;}}/*** 设置结果* @param response 结果对象* @throws IllegalStateException 如果结果已被设置*/public void complete(T response) {synchronized (this) {if (done) {throw new IllegalStateException("结果已被设置");}this.response = response;this.done = true;this.notifyAll();}}
}
一个使用等待通知机制的例子
信箱类:借助HashMap用于管理收信人id与对应的异步结果对象GuardObject
收信人:借助信箱类的管理,设置对应的id与新建异步结果对象,接着调用对应的get进行等待。
邮递员:传递收件人的id,并设置对应的content,借助信箱类根据收件人id得到对应的GuardObject异步结果对象,调用compete通知(将content传递)。
异步结果对象:两个方法,get/compete一个等待,一个通知,实现多线程的保护性暂停模式。
1 收信人
在收信人当中需要内置变量信箱,在初始化创建时就需要对应调用创建信箱的方法,然后重写run方法,因为我们需要实现的是每创建一个收信人就需要新建一个线程执行业务代码,run方法当中使用get进行等待,若邮递员发送通知之后再将结果接收,接收成功之后还需从信箱当中移除。
/*** 模拟收信人*/
class People extends Thread {private final GuardObject<String> guardObject;public People() {super("People-" + System.currentTimeMillis());this.guardObject = MailBox.createGuardObject();}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "开始收信...");try {// 等待信件,带超时String response = guardObject.get(5000);System.out.println(Thread.currentThread().getName() + "收到信:" + response);} catch (TimeoutException e) {System.out.println(Thread.currentThread().getName() + "收信超时");} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "收信被中断");Thread.currentThread().interrupt(); // 恢复中断状态} finally {// 确保从MailBox中移除GuardObjectMailBox.removeGuardObject(guardObject.getId());}}
}
2 邮递员
邮递员需要内置两个成员变量,一个是收信人的id用来获取寻找对应的收信人,同时也是开启新的线程继承Thread类重写run方法,首先获取收信人的对象,调用compete方法通知对应的收信人
/*** 邮递员类*/
class PostMan extends Thread {private final int id;private final String content;public PostMan(int id, String content) {super("PostMan-" + id);this.id = id;this.content = content;}@Overridepublic void run() {GuardObject<String> guardObject = MailBox.getGuardObject(id);if (guardObject == null) {System.out.println(Thread.currentThread().getName() + "错误:收信人不存在");return;}System.out.println(Thread.currentThread().getName() + "开始发送信件...");try {// 模拟投递延迟Thread.sleep(1000 + (int)(Math.random() * 2000));// 发送信件guardObject.complete(content);System.out.println(Thread.currentThread().getName() + "已发送信件");} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "发送被中断");Thread.currentThread().interrupt(); // 恢复中断状态} catch (IllegalStateException e) {System.out.println(Thread.currentThread().getName() + "错误:" + e.getMessage());}}
}
3 信箱类
信箱当中维护的是一个HashMap集合,id存储收件人信息,id,GuardObject<String>对象
/*** 用于管理多个 GuardObject 的信箱类*/
class MailBox {private static final AtomicInteger idGenerator = new AtomicInteger(1);private static final Map<Integer, GuardObject<?>> map = new ConcurrentHashMap<>();/*** 创建并返回一个泛型 GuardObject*/public static <T> GuardObject<T> createGuardObject() {int id = idGenerator.getAndIncrement();GuardObject<T> guardObject = new GuardObject<>(id);map.put(id, guardObject);return guardObject;}/*** 获取所有 GuardObject 的 ID*/public static Set<Integer> getGuardObjectIds() {return map.keySet();}/*** 根据id获取GuardObject*/@SuppressWarnings("unchecked")public static <T> GuardObject<T> getGuardObject(int id) {return (GuardObject<T>) map.get(id);}/*** 移除GuardObject*/public static void removeGuardObject(int id) {map.remove(id);}
}
4 保护对象
这里同时也会根据id维护独立的GuardObject对象,里面实现了get与compete的逻辑代码
/*** 增强版保护对象类*/
class GuardObject<T> {private T response;private boolean done = false;private final int id;public GuardObject(int id) {this.id = id;}public int getId() {return id;}/*** 获取结果(带超时)*/public T get(long timeout) throws InterruptedException, TimeoutException {synchronized (this) {long start = System.currentTimeMillis();long remaining = timeout;while (!done) {if (timeout > 0 && remaining <= 0) {throw new TimeoutException("等待结果超时");}if (timeout == 0) {this.wait();} else {this.wait(remaining);remaining = timeout - (System.currentTimeMillis() - start);}}return response;}}/*** 设置结果*/public void complete(T response) {synchronized (this) {if (done) {throw new IllegalStateException("结果已被设置");}this.response = response;this.done = true;this.notifyAll();}}
完整代码
package day01.mysynchronized;import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;public class Example7 {public static void main(String[] args) throws InterruptedException {// 创建多个收信人for (int i = 0; i < 3; i++) {new People().start();}// 等待收信人创建GuardObjectThread.sleep(1000);// 获取所有等待收信的IDSet<Integer> ids = MailBox.getGuardObjectIds();// 为每个收信人创建邮递员int mailCount = 1;for (int id : ids) {new PostMan(id, "信件内容" + mailCount++).start();}}
}/*** 模拟收信人*/
class People extends Thread {private final GuardObject<String> guardObject;public People() {super("People-" + System.currentTimeMillis());this.guardObject = MailBox.createGuardObject();}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "开始收信...");try {// 等待信件,带超时String response = guardObject.get(5000);System.out.println(Thread.currentThread().getName() + "收到信:" + response);} catch (TimeoutException e) {System.out.println(Thread.currentThread().getName() + "收信超时");} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "收信被中断");Thread.currentThread().interrupt(); // 恢复中断状态} finally {// 确保从MailBox中移除GuardObjectMailBox.removeGuardObject(guardObject.getId());}}
}/*** 邮递员类*/
class PostMan extends Thread {private final int id;private final String content;public PostMan(int id, String content) {super("PostMan-" + id);this.id = id;this.content = content;}@Overridepublic void run() {GuardObject<String> guardObject = MailBox.getGuardObject(id);if (guardObject == null) {System.out.println(Thread.currentThread().getName() + "错误:收信人不存在");return;}System.out.println(Thread.currentThread().getName() + "开始发送信件...");try {// 模拟投递延迟Thread.sleep(1000 + (int)(Math.random() * 2000));// 发送信件guardObject.complete(content);System.out.println(Thread.currentThread().getName() + "已发送信件");} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + "发送被中断");Thread.currentThread().interrupt(); // 恢复中断状态} catch (IllegalStateException e) {System.out.println(Thread.currentThread().getName() + "错误:" + e.getMessage());}}
}/*** 用于管理多个 GuardObject 的信箱类*/
class MailBox {private static final AtomicInteger idGenerator = new AtomicInteger(1);private static final Map<Integer, GuardObject<?>> map = new ConcurrentHashMap<>();/*** 创建并返回一个泛型 GuardObject*/public static <T> GuardObject<T> createGuardObject() {int id = idGenerator.getAndIncrement();GuardObject<T> guardObject = new GuardObject<>(id);map.put(id, guardObject);return guardObject;}/*** 获取所有 GuardObject 的 ID*/public static Set<Integer> getGuardObjectIds() {return map.keySet();}/*** 根据id获取GuardObject*/@SuppressWarnings("unchecked")public static <T> GuardObject<T> getGuardObject(int id) {return (GuardObject<T>) map.get(id);}/*** 移除GuardObject*/public static void removeGuardObject(int id) {map.remove(id);}
}/*** 增强版保护对象类*/
class GuardObject<T> {private T response;private boolean done = false;private final int id;public GuardObject(int id) {this.id = id;}public int getId() {return id;}/*** 获取结果(带超时)*/public T get(long timeout) throws InterruptedException, TimeoutException {synchronized (this) {long start = System.currentTimeMillis();long remaining = timeout;while (!done) {if (timeout > 0 && remaining <= 0) {throw new TimeoutException("等待结果超时");}if (timeout == 0) {this.wait();} else {this.wait(remaining);remaining = timeout - (System.currentTimeMillis() - start);}}return response;}}/*** 设置结果*/public void complete(T response) {synchronized (this) {if (done) {throw new IllegalStateException("结果已被设置");}this.response = response;this.done = true;this.notifyAll();}}
}
结果展示
2 顺序控制模式
顺序控制是同步模式中的一种重要控制方式,它确保多个操作或任务按照预定的顺序执行。
(1 利用wait/notify
package day01.tongbu;public class example1 {static final Object lock = new Object();//用于标记线程2是否已经执行完毕static boolean flag = false;public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (lock) {while (!flag) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("t1 线程被唤醒,执行剩下的业务");}});Thread t2 = new Thread(() -> {synchronized (lock) {System.out.println("t2 线程执行完毕");flag = true;lock.notify();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}
(2 使用ReentrantLock
package day01.tongbu;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class example3 {static boolean flag = false;static final ReentrantLock lock = new ReentrantLock();static final Condition condition = lock.newCondition();public static void main(String[] args) {// 实现先执行线程2,再执行线程1Thread t1 = new Thread(() -> {// 获取锁,未获取将阻塞lock.lock();try {while (!flag) {System.out.println("线程1等待线程2");condition.await();}Thread.sleep(500);System.out.println("线程1开始执行");Thread.sleep(500);System.out.println("线程1完成工作");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}});Thread t2 = new Thread(() -> {lock.lock();try {System.out.println("线程2开始执行");Thread.sleep(500);System.out.println("线程2执行完毕");flag = true;condition.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}
(3 借助park/unpark
package day01.tongbu;import java.util.concurrent.locks.LockSupport;public class example4 {public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println("t1等待....");LockSupport.park();System.out.println("t1运行");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t1结束");});Thread t2 = new Thread(() -> {System.out.println("t2开始运行");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t2结束并标记t1");LockSupport.unpark(t1);});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}
(对比
特性 | synchronized + wait/notify | ReentrantLock + Condition | LockSupport |
---|---|---|---|
实现复杂度 | 低 | 中 | 低 |
性能 | 中等 | 高 | 最高 |
灵活性 | 低 | 高 | 中 |
多条件支持 | ❌ | ✅ | ❌ |
公平锁选项 | ❌ | ✅ | ❌ |
超时控制 | ❌ | ✅ | ✅ |
唤醒先于阻塞 | ❌ | ❌ | ✅ |
锁获取尝试 | ❌ | ✅ | ❌ |
内存占用 | 低 | 中 | 低 |
适用场景 | 简单同步 | 复杂同步 | 简单阻塞/唤醒 |
(4 实现多个线程交替执行
使用ReentractLock
package day01.tongbu;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class example5 {static ReentrantLock lock = new ReentrantLock();static Condition conditionA = lock.newCondition();static Condition conditionB = lock.newCondition();static Condition conditionC = lock.newCondition();static int flag = 1;static final int MAX_LOOP = 5; // 控制打印5次ABCpublic static void main(String[] args) {Thread t1 = new Thread(() -> {lock.lock();try {for (int i = 0; i < MAX_LOOP; i++) {while (flag != 1) {conditionA.await();}System.out.print("A");flag = 2;conditionB.signal();}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}});Thread t2 = new Thread(() -> {lock.lock();try {for (int i = 0; i < MAX_LOOP; i++) {while (flag != 2) {conditionB.await();}System.out.print("B");flag = 3;conditionC.signal();}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}});Thread t3 = new Thread(() -> {lock.lock();try {for (int i = 0; i < MAX_LOOP; i++) {while (flag != 3) {conditionC.await();}System.out.print("C");flag = 1;conditionA.signal();}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}});t1.start();t2.start();t3.start();// 等待所有线程执行完毕try {t1.join();t2.join();t3.join();} catch (InterruptedException e) {e.printStackTrace();}}
}
模块化
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class example5 {static ReentrantLock lock = new ReentrantLock();static Condition[] conditions = new Condition[3];static int currentFlag = 0; // 0 -> A, 1 -> B, 2 -> Cstatic final int MAX_LOOP = 5;public static void main(String[] args) {// 初始化每个 conditionfor (int i = 0; i < conditions.length; i++) {conditions[i] = lock.newCondition();}// 启动三个线程Thread t1 = new PrintThread(0, 'A', 1).getThread();Thread t2 = new PrintThread(1, 'B', 2).getThread();Thread t3 = new PrintThread(2, 'C', 0).getThread();t1.start();t2.start();t3.start();// 等待全部完成try {t1.join();t2.join();t3.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("全部打印完成!");}static class PrintThread {private final int id;private final char toPrint;private final int nextId;public PrintThread(int id, char toPrint, int nextId) {this.id = id;this.toPrint = toPrint;this.nextId = nextId;}public Thread getThread() {return new Thread(() -> {lock.lock();try {for (int i = 0; i < MAX_LOOP; i++) {while (currentFlag != id) {conditions[id].await();}System.out.println(toPrint);currentFlag = nextId;conditions[nextId].signal();}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}});}}
}
3 生产者消费者模式
消费者与生产者之间的协调关系,为了解决生产过剩的情况或者说消费过多,这里的缓冲区相当于就是一个物品的储物箱,达到最大说明生产者生产太多了,达到最小说明消费者消费太多了。当达到最大就让生产者进入等待队列,唤醒消费者,反之就让消费者进入等待队列,唤醒生产者。
借助ReentrantLock与条件变量
package day01.lockax;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class Conditional {private final ReentrantLock lock = new ReentrantLock();private final Condition notEmpty = lock.newCondition(); // 消费者等待条件private final Condition notFull = lock.newCondition(); // 生产者等待条件private final int[] buffer = new int[5]; // 缓冲区private int count = 0; // 当前缓冲区元素数量// 生产者方法:向缓冲区添加元素public void produce(int item) throws InterruptedException {lock.lock();try {// 如果缓冲区已满,等待while (count == buffer.length) {System.out.println("缓冲区已满,生产者等待...");notFull.await(); // 释放锁并等待}// 添加元素到缓冲区buffer[count++] = item;System.out.println("生产者生产了: " + item + ",当前缓冲区大小: " + count);// 唤醒消费者notEmpty.signal();} finally {lock.unlock();}}// 消费者方法:从缓冲区取出元素public void consume() throws InterruptedException {lock.lock();try {// 如果缓冲区为空,等待while (count == 0) {System.out.println("缓冲区为空,消费者等待...");notEmpty.await(); // 释放锁并等待}// 从缓冲区取出元素int item = buffer[--count];System.out.println("消费者消费了: " + item + ",当前缓冲区大小: " + count);// 唤醒生产者notFull.signal();} finally {lock.unlock();}}// 测试生产者-消费者模型public static void main(String[] args) {Conditional example = new Conditional();// 启动生产者线程Thread producer = new Thread(() -> {for (int i = 1; i <= 10; i++) {try {example.produce(i);Thread.sleep(500); // 模拟生产耗时} catch (InterruptedException e) {e.printStackTrace();}}});// 启动消费者线程Thread consumer = new Thread(() -> {for (int i = 1; i <= 10; i++) {try {example.consume();Thread.sleep(800); // 模拟消费耗时} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();consumer.start();}
}
4 Balking模式(犹豫模式)
当某个线程发现另一个线程已经执行了相同的操作时,放弃当前操作并直接返回,避免重复操作导致的资源浪费或状态不一致。
Balking 模式的核心是 避免重复操作和无效操作,通过状态检查确保操作只在合适的状态下执行。
实现多次访问不再重复创建
package day01;public class ax5 {public static void main(String[] args) throws InterruptedException {TwoPhaseTermination tpt = new TwoPhaseTermination();tpt.start();tpt.start(); // 被balking拒绝Thread.sleep(3500);tpt.stop();// 测试重启功能Thread.sleep(500);tpt.start(); // 应能重新启动Thread.sleep(1500);tpt.stop();}
}class TwoPhaseTermination {private Thread monitor;// stop 这个状态是判断是否已经停止,防止调用stop时出错,而state状态用来判断当前是否正在运行private volatile boolean stop = false;private static final int NEW = 0;private static final int RUNNING = 1;private volatile int state = NEW;public synchronized void start() {if (state != NEW) {System.out.println(Thread.currentThread().getName() + ": 拒绝重复启动");return;}state = RUNNING;stop = false;monitor = new Thread(() -> {try {while (!stop) {try {Thread.sleep(1000);System.out.println("执行监控记录");} catch (InterruptedException e) {System.out.println("睡眠中被中断,准备退出");}}} finally {synchronized (TwoPhaseTermination.this) {state = NEW; // 关键修改:重置为NEW而非TERMINATED}System.out.println("料理后事,释放资源");}}, "monitor");monitor.start();}public synchronized void stop() {if (state != RUNNING) return;stop = true;if (monitor != null) {monitor.interrupt();}}
}
二 线程管理/生命周期模式
1 两阶段终止模式
两阶段终止模式(Two-Phase Termination Pattern)是一种多线程编程中安全终止线程的设计模式。它的核心思想是:在终止线程前,先发出终止请求(第一阶段),然后线程在完成必要清理工作后再真正终止(第二阶段)。这种模式避免了直接强制终止线程导致的资源泄露、数据不一致等问题
简单来说就是在一个线程T1中如何优雅的终止线程T2,给T2一个料理后事的机会
在某些线程正在运行时如何正确终止线程,如果强制stop会带来相对应的问题:
- 1 资源未释放:线程可能持有锁或者打开文件或者网络连接未关闭
- 2 数据不一致:线程可能正在修改共享数据,突然终止会导致数据损坏。
- 3 不可控性:无法保证线程在安全点终止。
(1 使用interrupt
需要借助interrupt进行打断,存在一个中断标志位,可以在这个中断循环之外将其他需要善后的地方再进行操作,可实现优雅终止。在下面代码实现的过程当中需要注意是在睡眠当中还是在运行当中被打断。
代码当中先开启工作线程,线程两个判断,如果isInterrupted==true就是执行了interrupt()将线程打断(模拟将后事料理),而另一种情况就是没有被打断就休眠1s(模拟工作过程),主线程先休眠再打断工作线程。
package day01;public class ax5 {public static void main(String[] args) {TwoPhaseTermination tpt = new TwoPhaseTermination();tpt.start();try {Thread.sleep(3500);} catch (InterruptedException e) {System.out.println("main thread stop");}tpt.stop();}
}class TwoPhaseTermination {private Thread monitor;public void start() {monitor = new Thread(() -> {while (true) {Thread current = Thread.currentThread();if (current.isInterrupted()) {System.out.println("打断线程,料理后事");break;} else {try {Thread.sleep(1000);System.out.println("执行监控记录");} catch (InterruptedException e) {current.interrupt();// 重置标志位位}}}});monitor.start();}public void stop() {monitor.interrupt();}
}
补充:
interrupted() | 静态方法 | 当前线程(调用 Thread.interrupted() ) |
isInterrupted() | 实例方法 | 指定线程对象(如 thread.isInterrupted() ) |
interrupted() | - 当前线程需要检查中断状态并主动清除标志(如处理完中断后)。 |
- 需要确保中断状态不会被后续逻辑误判(如循环中检查中断状态)。 | |
isInterrupted() | - 检查其他线程的中断状态(如监控子线程是否被中断)。 |
- 需要多次检查中断状态且保留标志位(如在循环中持续监控)。 |
(2 使用volatile改进两阶段终止模式
设置一个volatile共享变量,实现线程之间共享
package day01;public class ax5 {public static void main(String[] args) {TwoPhaseTermination tpt = new TwoPhaseTermination();tpt.start();try {Thread.sleep(3500);} catch (InterruptedException e) {System.out.println("main thread stop");}tpt.stop();}
}class TwoPhaseTermination {private Thread monitor;// 保证共享变量在多线程之间的可见性private volatile boolean flag = false;public void start() {monitor = new Thread(() -> {while (true) {Thread current = Thread.currentThread();if (flag) {System.out.println("打断线程,料理后事");break;} else {try {Thread.sleep(1000);System.out.println("执行监控记录");} catch (InterruptedException e) {// 重置标志位位current.interrupt();}}}});monitor.start();}public void stop() {flag = true;// 打断监控线程(中断在睡眠期间的线程,不加的话需要等待睡眠结束)monitor.interrupt();}
}
核心模式概括:
-
两阶段终止模式 (Two-Phase Termination Pattern)
-
干什么的: 优雅地终止一个正在运行的线程。
-
核心思想:
-
阶段一:发出终止请求 (
interrupt()
或设置标志位) - 不是立即强制停止,而是通知目标线程“该准备停止了”。 -
阶段二:线程处理终止请求 - 目标线程在安全点(通常是检查中断状态或标志位的地方)检测到终止请求后,主动进行必要的清理工作(如释放锁、关闭文件、保存状态等),然后自行结束运行。
-
-
关键点: 避免使用
stop()
等强制终止方法,确保线程资源被正确释放,保证数据一致性。
-
-
同步模式之顺序控制模式 (Sequential Control Pattern)
-
干什么的: 控制多个线程的执行顺序,确保某些操作按照特定次序发生。
-
核心思想: 利用同步机制(锁、条件变量)或专门的同步工具(
CountDownLatch
,CyclicBarrier
,Phaser
,Semaphore
)来协调线程间的执行步调。 -
常见场景:
-
线程A必须在线程B完成操作X之后才能开始操作Y。 (例如:使用
CountDownLatch(1)
,B完成时countDown()
,A在操作Y前await()
)。 -
多个线程必须同时到达某个点才能继续执行。 (例如:使用
CyclicBarrier(N)
)。 -
限制同时访问某个资源的线程数量。 (例如:使用
Semaphore
实现限流)。
-
-
关键点: 解决线程执行的“先后”或“同时”问题。
-
-
同步模式之保护性暂停模式 (Guarded Suspension Pattern)
-
干什么的: 一个线程需要等待另一个线程提供特定条件(通常是某个结果)满足后才能继续执行。
-
核心思想:
-
定义一个受保护的对象 (
GuardedObject
),它包含条件变量(判断条件是否满足)和结果。 -
等待线程: 在一个循环中检查条件是否满足。如果不满足,则挂起等待 (
wait()
)。 -
执行线程: 当条件满足(结果准备好)时,设置结果并通知 (
notify()/notifyAll()
) 所有等待的线程。 -
等待线程被唤醒后,再次检查条件(避免虚假唤醒),如果满足则获取结果并继续执行。
-
-
关键点: 解决线程间的结果依赖和条件等待问题,是
Future/Promise
模式的基础。避免忙等待(Busy Waiting)。
-
常见并发设计模式总结列表:
以下是一些在多线程/并发编程中广泛使用的设计模式,它们解决了不同层面的并发问题:
-
线程管理/生命周期模式:
-
线程池模式 (Thread Pool): 预先创建一组线程并复用它们来执行任务,避免频繁创建销毁线程的开销,便于管理并发度。
-
Worker Thread 模式: 是线程池模式的一种体现,每个工作线程从共享的任务队列中获取并执行任务。
-
Thread-Per-Message 模式: 为每个新请求或任务创建一个新线程来处理。简单但资源消耗大,不适合高并发。
-
两阶段终止模式 (Two-Phase Termination): (如前所述) 优雅地停止线程。
-
-
同步与协调模式:
-
保护性暂停模式 (Guarded Suspension): (如前所述) 等待条件/结果满足。
-
顺序控制模式 (Sequential Control): (如前所述) 控制线程执行顺序。
-
生产者-消费者模式 (Producer-Consumer): 使用有界队列 (
BlockingQueue
) 解耦生产数据的线程和消费数据的线程。生产者放数据,消费者取数据,队列满时生产者阻塞,队列空时消费者阻塞。 -
读写锁模式 (Readers-Writer Lock): 允许多个线程同时读共享资源,但只允许一个线程写。在读多写少的场景下提高并发性能。
-
Balking 模式 (犹豫模式): 当对象处于不适当状态时,立即放弃执行某个操作。例如,一个初始化方法如果发现对象已经初始化,就直接返回。通常用状态标志判断。
-
Thread-Specific Storage 模式 (线程局部存储/ThreadLocal): 为每个线程提供其自己的变量副本,避免共享变量的同步开销。用于存储线程上下文信息(如数据库连接、用户会话)。
-
-
避免共享/状态管理:
-
不变性模式 (Immutable Object): 创建状态不可变的对象。这种对象天生线程安全,无需同步即可被多个线程安全访问。
-
Copy-on-Write 模式: 在修改共享数据时,先创建一份副本,在副本上修改,修改完成后再原子性地替换引用。常用于读多写少的集合(如
CopyOnWriteArrayList
)。
-
-
异步编程模式:
-
Future / Promise 模式: 保护性暂停模式的更高级抽象。
Future
代表一个异步操作的结果占位符,允许在结果准备好之后再去获取它(可能阻塞等待)。Promise
是结果的提供者,用于设置最终结果(或异常)。它们是现代异步编程(如CompletableFuture
)的基础。
-
总结要点:
-
两阶段终止: 关注线程如何安全结束。
-
顺序控制: 关注线程执行的先后/同步点。
-
保护性暂停: 关注一个线程等待另一个线程的结果/条件。
-
模式选择: 选择哪种模式取决于你面临的具体并发问题(资源管理、执行顺序、数据共享、结果依赖、状态管理等)。理解这些模式的核心思想和适用场景是设计健壮高效并发程序的关键。
-
组合使用: 实际应用中,这些模式经常被组合使用(例如,线程池中的工作线程可能使用保护性暂停等待任务,任务处理可能涉及生产者-消费者队列,终止时使用两阶段终止)。