首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

多线程开发之如何创建一个线程安全的类

上一篇讨论了如何解决线程安全的问题,今天总结如何设计一个线程安全的类;

创建线程安全类的关注点

一个类要想线程安全,除了上一篇文章通过外部解决方式外,还可以通过合理的设计类的内部来解决,使类本身就线程安全,那么要怎么才能使类是线程安全的呢?

类不是线程安全的原因主要就是它包含了一些属性,这些属性是这个类实例对象的变量,这些变量影响着对象状态,由于对这些属性的访问在多线程情况下出现一些不安全使得对象状态并不符合预期导致类的不安全,所以设计线程安全类大概方向就是保证这些影响对象状态的变量,在Java并发编程实战中的总结如下:

找出构成对象状态的所有变量;

找出约束状态变量的不变性条件;

建立对象状态的并发访问管理策略;

这里先解释下不变性条件,在一些类的它的一些变量的变化是有一定规则的,比如类中定义一个属性表示苹果卖出了多少斤,卖出就增加,退货就减少,但是它肯定不会是一个负值,不为负值这就是这个变量的不变性条件。在比如类中定义了最大值与最小值,那么在这两个变量只有有一个不变性条件就是最大值要大于等于最小值。

简单解释了不变性条件再来理解下上面3条:可以把这3条分成3个步骤,首先是找出构成对象状态的所有变量,第二步是找出变量的约束条件,最后是上面两步找出的变量进行并发访问控制保证不变性条件的约束,对比较独立的属性直接进行并发访问,但是对有关联的那就必须要更多的机制保证这个约束。

所以我们可以把设计线程安全的类步骤分成以下三块:

1、找出所有需要同步的属性,保证不可变条件和后验条件(方法执行后必须为真的条件),比如上面举的卖出苹果的重量,比如最大值与最小值关系;

2、保证一些依赖一些状态的操作正确执行(先验条件方法执行前必须为真的条件),比如单例模式中实体为null的时候才初始化;

3、一定要控制这些属性的所有权,基础类型的属性可能比较好控制,有些是引用类型但又要保证线程安全的,那就要严格控制所有权,否则有可能其他线程拿到这个引用进行修改这个对象的内容,造成线程不安全;

已有对象如何保证线程安全

我们可以设计出线程安全的类,但是有可能有些对象已经存在,然后它并不是线程安全,现在却需要保证它的线程安全,那该如何做呢?

通过监视器模式实现,把对象封装到一个新对象里面,所有对这个对象的访问都通过新对象的方法访问,然后保证新对象的方法是线程安全的就行了。

并不一定需要自己实现线程安全

但是有时候我们并不是一定要设计成线程安全的类,如果已经存在一些线程安全的类可以保证我们需要的线程安全,还是要尽量用现有的,比如上面提到过苹果卖出的重量,就可以利用AtomicInteger来保证安全,再比如一些缓存也可以用ConcurrentMap来保证线程安全,把我们需要保证的线程安全委托给一些线程安全的类。

但是委托并不是一定有用的,比如前面的最大、最小值例子,如果定义成现AtomicInteger也无法保证它们的不可变条件的约束!这种可能就只能加锁了,但是如果他们两个并没有不可变条件约束是两个无关的共享变量,还是可以把多个无关的状态变量委托给线程安全的类

如何扩展线程安全类

那么如果一个线程安全的类功能不满足我们的需求,需要扩展一些功能,可是又不能修改这个类,那么就必须要对这个类进行扩展,扩展分两种方法继承、客户端扩展。继承实现比较简单,继承的类只要在保证新增的方法是线程安全的,那么它整个都是线程安全的,不过客户端扩展可能情况复杂一点,一个错误的例子如下图:

新增的方法虽然加了锁,可是它是加在ListExpand这个类的对象上,而list方法里面的锁则是在list这个对象上,所有整体并不是线程安全的。是应该在方法内部然后锁list这个对象!

还可以把一些线程不安全的类通过继承然后重写方法实现线程安全,不过这和前面提到的通过监视器模式实现比较相似。

总结

从如何实现线程安全的类到线程不安全类如何保证线程安全,然后是如何利用已有线程安全类实现线程安全,当线程安全类不满足需求时又该如何扩展,这四个方面都进行了梳理。

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200616A0WHG100?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券