[Java 并发] 并发编程实战笔记-对象的组合

编写线程安全的代码时,我们不希望对每次内存访问都进行分析一确保程序是线程安全的,而是希望将一些现有的安全组建组合为更大规模的组建或程序。下面介绍一些组合模式,这些模式能够使一个类更容易成为线程安全的,并且在维护这些类是不会无意中破坏类的安全性保证的。

设计线程安全的类

在设计线程安全类的过程中,需要包含以下三个基本要素:

  • 找出构成对象状态的所有变量。
  • 找出约束状态变量的不变性条件。
  • 建立对象状态的并发访问管理策略。

对象的状态是由对象的域组成的,有0-n个不等,如果域都是基本类型,那这些域构成对象的全部状态,如果有引用类型,那么该对象的状态包括被引用对象的域(如LinkedList的状态包括链表中所有节点对象的状态)。

实例封装

如果对象不是线程安全的额,那么可以通过多种技术使他在多线程程序中安全的使用。

  1. 可以确保该对象只能由单个线程访问(线程封闭),如JDBC Connection对对象,ThreadLocal。
  2. 通过一个锁来保护该对象的所有访问(Java监视器模式),如PersonSet,代码在后面。
  3. 封装对象,只暴露可访问的方法。与对象由整个程序访问的情况比,更容易对代码进行分析。如Collections中的UnmodifiableCollection。
// mySet不会逸出,唯一的外部引用就是PersonSet,使用Java监视器来封装能确保线程安全。
@ThreadSafe
    public class PersonSet {
        @GuardedBy("this")
        private final Set<Person> mySet = new HashSet<Person>();
        public synchronized void addPerson(Person p) {
            mySet.add(p);
        }
        public synchronized boolean containsPerson(Person p) {
            return mySet.contains(p);
        }
    }

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程的访问数据室总能持有正确的锁。

线程安全性的委托

我们可以把线程安全委托给先有的线程安全类,这样我们的代码阿九不用关心线程安全的问题了额。这里有两种情况:

  1. 如果委托给单独的线程安全类,能保证线程安全。如,我们可以使用ConcurrentHashMap保存线程共享数据。
  2. 如果委托给两个或两个已上的线程安全类,如果存在竞态条件,需要额外的同步机制保证;如果分别表示独立的状态,可以不使用额外的同步机制即可保证线程安全。
  3. 在现有线程安全类中添加功能,叫作客户端加锁。这种机制是派生类的行为与基类耦合在一起,破坏了基类的同步策略,使用时需要特别小心

示例代码:

// 情况2,需要增加同步机制保证 check-than-act
public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);
    public void setLower(int i) {
        // Warning -- unsafe check-then-act , need a lock
        if (i > upper.get())
            throw new IllegalArgumentException(
                    "can't set lower to " + i + " > upper");
        lower.set(i);
    }
    public void setUpper(int i) {
        // Warning -- unsafe check-then-act , need a lock
        if (i < lower.get())
            throw new IllegalArgumentException(
                    "can't set upper to " + i + " < lower");
        upper.set(i);
    }
    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

小结

这一章介绍了实现线程安全类是采用的一些技术。

  1. 线程安全可以委托给现有的线程安全类。
  2. 委托是创建线程安全的一个有效策略。
  3. 值需要让现有的线程安全类管理所有的状态即可
  4. 当需要使用多个线程安全类保存状态是,需要额外的同步机制保证。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,779评论 18 399
  • layout: posttitle: 《Java并发编程的艺术》笔记categories: Javaexcerpt...
    xiaogmail阅读 5,876评论 1 19
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,375评论 11 349
  • 今天晚上和老二去接姐姐的路上,宝贝问:妈妈你看到QQ上老师发的视频了?”我说:“没看啊,今天妈妈去商场了,...
    孙若菡妈妈阅读 233评论 0 0
  • “一个人说正经话,说得不对可以劝他;一个人在胡言乱语,何劝之有?” 因都不爱说话,两人倒能说到一起。 是让小谢明白...
    Wisconsin0607阅读 371评论 0 0