良好的并发编程习惯之封闭(Confinement)

打印 上一主题 下一主题

主题 790|帖子 790|积分 2380

创作内容丰富的干货文章很费心力,感谢点过此文章的读者,点一个关注鼓励一下作者,鼓励他分享更多的精彩好文,谢谢各人!

“共享可变状态”有两个要点:“共享”和“可变”。封闭的策略是:不共享就完事了。
《Java 并发编程实战》一书中列举了三种封闭的方式。


  • Ad-hoc 线程封闭
  • 栈封闭
  • ThreadLocal 类
Ad-hoc 封闭

"Ad-hoc" 一样平常指“特别的、专门的、暂时的”等,在编程的语境中一样平常指“详细环境详细分析”。Ad-hoc 封闭也就指由程序自己实现的封闭。
例如有个 volatile 变量,在编写代码的时间,隐含实现了如许的约定:只有一个线程会“写”该变量,别的线程只会“读”利用。那么这种环境下这个“写线程”即使做了 "Check-then-Act" 利用也是线程安全的。
所以 Ad-hoc 封闭也只能是“详细环境详细分析”了。
栈封闭

局部变量(local variables)在方法调用时被分配到栈上,正常环境下当方法返回时就被销毁(不再被引用,可以被 GC 接纳),只存在于调用的线程中。这些变量由于不会被共享,即使变量自己并不是线程安全的,也不消担心方法的线程安全性。
固然如果局部变量通过一些方式在方法调用结束后仍然被引用,则不再是“封闭”的,就会有线程安全的问题。如变量被作为方法的返回值被返回;被方法里创建的线程引用;引用被保存到了别的地方,如实例变量(instance variable)等。
一样平常如果一个方法只依赖它的输入参数和方法内创建的局部变量,不依赖别的的全局的信息,则可以说这个方法是“无状态”的。
ThreadLocal 类

ThreadLocal 也可以认为是前文所说的“线程安全类”,只不外 ThreadLocal 的语义上就是“线程封闭”的。
ThreadLocal 的作用是为每个线程保存一个副本,每个线程在调用 get 或 set 方法时都只会利用本线程的副本。由于每个线程只用自己的那份,不存在共享行为,因此是线程安全的。
一样平常来说,如果有一些对象从作用是可以做成单例,但它自己又不是线程安全的,就可以利用 ThreadLocal 为每个线程创建一个副本,就可以线程安全地把它作为单例利用了。
例如,我们知道 SimpleDateFormat 不是线程安全的,但是通过 ThreadLocal 的包装,就可以做到线程封闭,不在线程间共享,做到线程安全,如下示例:
  1. public class DateUtil {
  2.   private static ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal
  3.           .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
  4.   public static String formatDate(Date date) throws ParseException {
  5.     return dateFormat.get().format(date);
  6.   }
  7. }
复制代码

要注意的是,由于须要为每个线程创建一个副本,如果初始化的代价比较高且经常性地创建新的线程,大概会有潜伏的性能问题,虽然通常环境下不会成为问题。
别的,不要把从 ThreadLocal 获取的引用保存到别的地方,会有潜伏的线程安全问题。
小结

封闭策略用人话来说就是:只管不要用全局变量,如果全局变量是单例,考虑用 ThreadLocal 包装。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

来自云龙湖轮廓分明的月亮

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表