Design Patterns

"我得学设计模式"

Posted by HZY on January 28, 2026

两阶段中止模式

  • 也可以使用volatile

同步模式

保护性暂停

用在一个线程等待另一个线程的执行结果

  • 需要关联同一个GuardedObject,实现一个结果从一个线程传递到另一个线程
  • 如果有结果源源不断从一个线程传递到另一个线程,那么就需要使用到消息队列
  • JDK中join future的实现就是使用这种模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class GuardedObject{
    Object response;
    public synchronized Object get(long timeout) throws InterruptedException {
        long begin = System.currentTimeMillis();
        long passtime = 0;
        while(response == null){
            long waittime = timeout - passtime;
            if (waittime<=0) break;
            wait(waittime);
            passtime = System.currentTimeMillis()-begin;
        }
        return response;
    }

    public synchronized void set(Object response){
        this.response = response;
    }
}

生产者消费者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class MsgQueue{
    private int capcity;
    private LinkedList<Message> list = new LinkedList<Message>();

    public MsgQueue(int capacity){
        this.capcity = capacity;
    }

    public Message take(){
        synchronized(list){
            while(!list.isEmpty()){
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        Message message = list.removeFirst();
        list.notifyAll();
        return message;
    }

    public void put(Message msg){
        synchronized(list){
            while(list.size()==capcity){
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        list.add(msg);
        list.notifyAll();
    }
}

final class Message{
    private int id;
    private Object value;

    public int getId() {
        return id;
    }

    public Object getValue() {
        return value;
    }
}

顺序控制

实现T2的结果在T1之后

  • wait-notify
  • park-unpark
    • 直接做成许可证式
    • 有点离奇
  • ReentrantLock

交替输出

  • wait-notify

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    
      public class Test26 {
          public static void main(String[] args) {
              WaitNotify waitNotify = new WaitNotify(1,5);
              new Thread(()->{
                  waitNotify.print("a",1,2);
              }).start();
              new Thread(()->{
                  waitNotify.print("b",2,3);
              }).start();
              new Thread(()->{
                  waitNotify.print("c",3,1);
              }).start();
    
          }
      }
    
      class WaitNotify {
          private int flag;
          private int loop_count;
    
          public WaitNotify(int flag, int loop_count) {
              this.flag = flag;
              this.loop_count = loop_count;
          }
    
          public void print(String str,int waitFlag,int nextFlag){
              for (int i = 0; i < loop_count; i++) {
                  synchronized (this) {
                      while (flag != waitFlag) {
                          try{
                              this.wait();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      System.out.println(str);
                      flag = nextFlag;
                      this.notifyAll();
                  }
              }
          }
    
      }
    
  • await-signal

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    
      public class Test27 {
          public static void main(String[] args) {
              AwaitSignal awaitSignal = new AwaitSignal(5);
              Condition a = awaitSignal.newCondition();
              Condition b = awaitSignal.newCondition();
              Condition c = awaitSignal.newCondition();
    
              new Thread(()->{
                  awaitSignal.print("a",a,b);
              }).start();
              new Thread(()->{
                  awaitSignal.print("b",b,c);
              }).start();
              new Thread(()->{
                  awaitSignal.print("c",c,a);
              }).start();
    
    
              awaitSignal.lock();
              a.signal();
              awaitSignal.unlock();
          }
      }
    
    
      class AwaitSignal extends ReentrantLock {
          private int loopNumber;
    
          public AwaitSignal(int loopNumber) {
              this.loopNumber = loopNumber;
          }
    
          public void print(String str, Condition current,Condition next) {
              for (int i = 0; i < loopNumber; i++) {
                  lock();
                  try{
                      current.await();
                      System.out.print(str);
                      next.signal();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }finally {
                      unlock();
                  }
              }
          }
      }
    
  • park-unpark

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
      public class Test28 {
          public static void main(String[] args) {
              ParkUnPark parkUnPark = new ParkUnPark(5);
    
              Thread[] threads = new Thread[3];
    
              threads[0] = new Thread(() -> {
                  parkUnPark.print("a", threads[1]);
              }, "t1");
    
              threads[1] = new Thread(() -> {
                  parkUnPark.print("b", threads[2]);
              }, "t2");
    
              threads[2] = new Thread(() -> {
                  parkUnPark.print("c", threads[0]);
              }, "t3");
    
              threads[0].start();
              threads[1].start();
              threads[2].start();
    
              LockSupport.unpark(threads[0]);
          }
      }
    
      class ParkUnPark {
          private final int loopNumber;
    
          public ParkUnPark(int loopNumber) {
              this.loopNumber = loopNumber;
          }
    
          public void print(String str, Thread next) {
              for (int i = 0; i < loopNumber; i++) {
                  LockSupport.park();
                  System.out.print(str);
                  LockSupport.unpark(next);
              }
          }
      }
    

犹豫模式

想让某件事情只做一次

  • 只用volatail不行
    • 不能保证原子性
  • 可以使用synchronized

单例模式

饿汉式

  • 加final
    • 害怕子类覆盖方法
  • 如何防止反序列化破坏单例
    • 重写 readResovle()方法
  • 设置私有
    • 防止别人new
    • 但不能防止反射
  • static final = new
    • 是线程安全的
  • 为何提供get方法,而不是直接给成员变量
    • 可以使用泛型

枚举实现

  • 是否为单例
  • 没有并发问题
  • 不能被反射破坏单例
  • 不能被 反序列化 破坏单例
  • 枚举单例是 饿汉式
  • 初始化可以用方法

懒汉式

  • double-check
    • 降低锁的粒度
  • 基于静态内部类
    • 只有用到才会触发类的加载

享元模式

  • 对对象进行重用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Slf4j
class Pool{
    private final int poolSize;

    private Connection[] connections;
    private AtomicIntegerArray states;

    public Pool(int poolSize) {
        this.poolSize = poolSize;
        this.connections = new Connection[poolSize];
        this.states = new AtomicIntegerArray(new int[poolSize]);
        for (int i = 0; i < poolSize; i++) {
            connections[i] = new MockConnection();
        }
    }

    public Connection borrow(){

        do {
            for (int i = 0; i < poolSize; i++) {
                if (states.get(i) == 0){
                    if(states.compareAndSet(i, 0, 1)) return connections[i];
                }
            }
            //
            synchronized (this) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
            }
        }while(true);
    }

    public void free(Connection con){
        for (int i = 0; i < poolSize; i++) {
            if (connections[i] == con){

                states.set(i, 0);
                synchronized (this) {
                    log.debug("归还连接 {}",con);
                    this.notifyAll();
                }
                break;
            }
        }
    }
}
  • 上面这个代码会有lost notification 的风险
    • 如果检查到第m个状态(m < n),但是m前面有con被释放
    • 这时候notify谁也不会打断,线程wait()之后没人来打断他