保护型暂停

image.png

代码:

  1. // 增加超时效果
  2. class GuardedObject {
  3. // 结果
  4. private Object response;
  5. // 获取结果
  6. public Object get() {
  7. synchronized (this) {
  8. while (response == null) {
  9. try {
  10. this.wait();
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. return response;
  16. }
  17. }
  18. // 产生结果
  19. public void complete() {
  20. synchronized (this) {
  21. // 给结果成员变量赋值,赋值成功后才唤醒另一个线程
  22. this.response = response;
  23. this.notifyAll();
  24. }
  25. }
  26. }

优势:

  • 比之join,好处是可以异步执行代码
  • join必须要定义全局变量,而Guarded Suspension可以用局部变量

终止模式之两阶段终止模式

在一个线程T1中,如何优雅终止线程T2?
这里的【优雅】指的是给T2正常关闭的时间。

思路:

  1. interrupt
  2. volatile
  1. @Slf4j(topic = "c.TwoPhaseTermination")
  2. class TwoPhaseTermination {
  3. // 监控线程
  4. private Thread monitorThread;
  5. // 停止标记(使用volatile,确保可见性,监控线程在循环的时候仍然可以看到值被修改)
  6. private volatile boolean stop = false;
  7. // 判断是否执行过 start 方法
  8. private boolean starting = false;
  9. // 启动监控线程
  10. public void start() {
  11. synchronized (this) {
  12. if (starting) { // false
  13. return;
  14. }
  15. starting = true;
  16. }
  17. monitorThread = new Thread(() -> {
  18. while (true) {
  19. Thread current = Thread.currentThread();
  20. // 是否被打断
  21. if (stop) {
  22. log.debug("料理后事");
  23. break;
  24. }
  25. try {
  26. Thread.sleep(1000);
  27. log.debug("执行监控记录");
  28. } catch (InterruptedException e) {
  29. }
  30. }
  31. }, "monitor");
  32. monitorThread.start();
  33. }
  34. // 停止监控线程
  35. public void stop() {
  36. stop = true;
  37. monitorThread.interrupt();
  38. }
  39. }

单例模式

很多场景都会用到单例模式,但是多并发下的单例模式会有很多问题发生

先看看一个线程不安全的:

  1. public final class Singleton {
  2. private Singleton() { }
  3. private static Singleton INSTANCE = null;
  4. // 分析这里的线程安全, 并说明有什么缺点
  5. public static synchronized Singleton getInstance() {
  6. if( INSTANCE != null ){
  7. return INSTANCE;
  8. }
  9. INSTANCE = new Singleton();
  10. return INSTANCE;
  11. }
  12. }
  • synchronize的范围太大,影响性能
  • INSTANCE并没有加volatile,可能会导致指令重排

改进

  1. public final class Singleton {
  2. private Singleton() { }
  3. // 加 volatile
  4. private static volatile Singleton INSTANCE = null;
  5. // 要点1
  6. public static Singleton getInstance() {
  7. if (INSTANCE != null) {
  8. return INSTANCE;
  9. }
  10. synchronized (Singleton.class) {
  11. // 要点2:为什么还要在这里加为空判断,上面不是判断过了吗
  12. if (INSTANCE != null) {
  13. return INSTANCE;
  14. }
  15. INSTANCE = new Singleton();
  16. return INSTANCE;
  17. }
  18. }
  19. }
  • 要点1:将方法上的synchronize卸下,将锁细化
  • 要点2:防止第一次初始化时,有多个线程进入,从而对上一个线程创建的单例对象进行覆盖赋值
  • 这个也叫DCL懒汉式(双重监测懒汉式),缩小了锁的范围,只有第一次会进入创建对象的方法,提高了效率。

推荐方法:

  1. public final class Singleton {
  2. private Singleton() { }
  3. // 问题1:属于懒汉式还是饿汉式
  4. private static class LazyHolder {
  5. static final Singleton INSTANCE = new Singleton();
  6. }
  7. // 问题2:在创建时是否有并发问题
  8. public static Singleton getInstance() {
  9. return LazyHolder.INSTANCE;
  10. }
  11. }
  • 问题一:静态内部类,只有当使用到的时候才会创建,所以是懒汉式
  • 问题二:静态方法会随着静态类加载,是由JVM负责,所以没有并发问题

异步模式之工作线程

让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现 就是线程池,也体现了经典设计模式中的享元模式

但注意,当线程池中线程设置为固定大小时,会发生饥饿问题,如何解决?

  • 不同的任务类型,采用不同的线程
  • 创建合适的线程数量

而如何创建合适的线程数量又是一个问题,根据线程的不同任务可大体分为两种

  • IO密集型
  • CPU密集型