以下是学习了《图解Java多线程设计模式》一书中记录的内容
Single Threaded Execution模式——能通过这座桥的只有一个人
Single Threaded Execution模式是多线程设计的基础。
Single Threaded Execution即“以一个线程执行”。就像独木桥同一时间只允许一个人通行一样。
Single Threaded Execution有时候也称为临界区(critical section)或临界域(critical region)。Single Threaded Execution这个名称侧重于执行处理的线程(过桥的人),而临界区或临界域的名称侧重于执行范围(人过的桥)。
该模式操作的角色就是SharedResource(共享资源),该类具有两种类型的方法:
1、多线程时:但线程程序不需要使用该模式,即无需使用synchronized,当然单线程使用synchronized也不会破坏安全性,但是性能会有所下降;
2、多个线程访问时:Share Resource角色的实例有可能被多个线程同时访问时;
3、状态有可能发生变化时:Share Resource角色的状态会发生变化,当然,如果实例创建后,实例的状态不会发生改变,例如Immutable模式中,则无需使用Single Threaded Execution模式;
4、需要确保安全性时:当然,只有在需要确保安全性时,才需要使用Single Threaded Execution模式;
tips:Java提供了针对集合类的线程安全方法如下
Collections.synchronizedCollection();
Collections.synchronizedList();
Collections.synchronizedMap();
Collections.synchronizedSet();
Collections.synchronizedSortedMap();
Collections.synchronizedSortedSet();
使用Single Threaded Execution会存在发生死锁的风险,发生死锁需要满足如下几个条件:
(1)存在多个ShareResource角色;
(2)线程在持有某个ShareResource角色的锁的同时,还想获取其他ShareResource角色的锁;
(3)获取ShareResource角色的锁的顺序并不固定(ShareResource角色是对称的);
如何防止死锁发生:只要破坏(1)、(2)、(3)中的一个条件即可。
假设要编写一个ShareResource的子类,如果子类能够访问ShareResource角色的字段,那么就有可能子类会出现unsafeMethod,即便能避免,那么在更多子类化的情况下还是会出现ShareResource的安全性问题。
在面向对象的程序设计中,伴随着子类话而出现的“继承”起着非常重要的作用。但对于多线程程序设计来说,继承会引起一些麻烦的问题,通常称之为继承反常(inheritance anomaly)。
Single Threaded Execution模式会降低程序性能,原因有两个方面:
1、获取锁花费时间:进入synchronized方法或者代码块时,线程获取对象锁花费时间
2、线程冲突引起的等待:当某一个线程执行临界区处理时,其他想要进入临界区的线程会阻塞。这种情况称之为线程冲突(conflict)。发生冲突时,程序的整体性能会随着线程等待时间的增加而下降。如果尽可能地缩小临界区的范围,降低线程冲突的概率,那么就能够抑制性能的下降。
tips:这里可以对比ConcurrentHashMap和Hashtable的底层实现,Hashtable所有方法都采用了Single Threaded Execution模式,而ConcurrentHashMap底层是基于分段锁。