关于Guarded Suspension模式
可以用“多线程版本的if”来理解Guarded Suspension模式,必须等到条件为真,但很多场景需要快速放弃
自动保存
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
| public class AutoSaveEditor { private boolean changed = false; private ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
@PostConstruct public void startAutoSave() { service.scheduleWithFixedDelay(() -> autoSave(), 5, 5, TimeUnit.SECONDS); }
public void edit() { changed = true; }
private void autoSave() { if (!changed) { return; } changed = false; save(); }
private void save() { } }
|
synchronized
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public void edit() { synchronized (this) { changed = true; } }
private void autoSave() { synchronized (this) { if (!changed) { return; } changed = false; } save(); }
|
- 共享变量changed是一个状态变量,业务逻辑依赖于这个状态变量的状态,本质上是if
- 在多线程领域,就是一种“多线程版本的if”,总结成一种设计模式,就是_Balking模式_
Balking模式
Balking模式本质上是一种规范化地解决“多线程版本的if”的方案
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
| public void edit() { change(); }
private void change() { synchronized (this) { changed = true; } }
private void autoSave() { synchronized (this) { if (!changed) { return; } changed = false; } save(); }
|
volatile + Balking模式
- 上面用synchronized实现Balking模式的方式最为稳妥,建议在实际工作中采用
- 如果对原子性没有要求,可以使用volatile(仅能保证可见性)来实现Balking模式
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
| public class RouterTable { private Map<String, CopyOnWriteArraySet<Router>> rt = new ConcurrentHashMap<>(); private volatile boolean changed; private ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
@PostConstruct public void startLocalSaver() { service.scheduleWithFixedDelay(this::autoSave, 1, 1, TimeUnit.MINUTES); }
private void autoSave() { if (!changed) { return; } changed = false; save2Local(); }
private void save2Local() { }
public void add(Router router) { CopyOnWriteArraySet<Router> routers = rt.computeIfAbsent(router.getIFace(), iFace -> new CopyOnWriteArraySet<>()); routers.add(router); changed = true; }
public void remove(Router router) { Set<Router> routers = rt.get(router.getIFace()); if (routers != null) { routers.remove(router); changed = true; } } }
|
单次初始化
Balking模式有一个非常典型的应用场景就是单次初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class SingleInit { private boolean inited = false;
public synchronized void init() { if (inited) { return; } doInit(); inited = true; }
private void doInit() { } }
|
单例模式
线程安全的单例模式本质上也是单次初始化,可以用Balking模式实现线程安全的单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton { private static Singleton singleton;
private Singleton() { }
public synchronized static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
|
双重检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Singleton { private static volatile Singleton singleton;
private Singleton() { }
public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
|
Guarded Suspension + Balking
- Balking模式只需要互斥锁就能实现,而Guarded Suspension模式则需要用到管程(高级并发原语)
- 从应用角度来看,两者都是为了解决“线程安全的if”
- Guarded Suspension模式会等待if条件为真(利用管程模型来实现),而Balking模式不会等待
参考资料
Java并发编程实战