美文网首页
[Java 并发] 并发编程实战笔记-对象的组合

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

作者: 但莫 | 来源:发表于2017-05-22 08:30 被阅读0次

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

    设计线程安全的类

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

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

    对象的状态是由对象的域组成的,有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. 当需要使用多个线程安全类保存状态是,需要额外的同步机制保证。

    相关文章

      网友评论

          本文标题:[Java 并发] 并发编程实战笔记-对象的组合

          本文链接:https://www.haomeiwen.com/subject/xywcxxtx.html