Java并发编程学习记录#4

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cysion1989/article/details/77689836

组合对象

探讨一些构造类的模式,使得类更容易成为线程安全的。

设计线程安全的类

设计线程安全的类的过程应该包含三个方面:
- 确定对象状态是由哪些变量构成–变量;
- 确定限制对象状态的不变约束–不变约束;
- 制定一个管理并发访问对象状态的策略–后验条件。

不变约束:用来判定一个状态是合法的还是不合法的,比如int的取值范围,是施加在状态上的约束;

后验条件:指出某种状态转变是否合法,是施加在状态操作上的约束;

上述二者需要引入额外的同步和封装。

组合:将一个对象封装到另一个对象内部。

组合使得被封装对象的全部访问路径都是可知的,这相比让整个应用系统 访问对象来说,更容易对访问路径进行分析,然后再和各种适当的锁相结合,可以确保程序能以线程安全的方式使用其它非线程安全的对象。

在并发领域,组合是为保证线程安全的的一个线程限制。

Java监视器模式

一种线程限制原则,遵循该原则的对象封装了所有的可变状态,并使用对象的内部锁来保护。例如:

public class PrivateLock {
    private final Object myLock = new Object();
    @GuardedBy("myLock") Widget widget;

    void someMethod() {
        synchronized (myLock) {
            // Access or modify the state of widget
        }
    }
}

委托线程安全

一些由线程安全的组件组合而成的组件未必是线程安全的。比如这些线程安全的子组件有依赖性。见代码:

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
        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
        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());
    }
}

若有两个线程,同时,分别访问setLower和setUpper,则可能出现线程安全问题。

只有当一个类有多个彼此独立的 线程安全的状态变量组合而成,且类的操作不包含任何无效的状态转换时,才可以将线程安全委托给这些状态变量。

向已有的线程安全类中添加功能

重用Java自带的线程安全类,要好于创建一个新的,无论在难度,风险或是维护上。比如对一个List添加功能:缺少则添加。即先判断list中是否有此元素,无,则添加。此时涉及到了检查-执行这一复合操作,按照之前同步策略,是可以在此操作上加锁将其变成原子性操作的,但是因为源码我们没法修改,只能找别的方式。这里,组合,或是继承都可以。比如继承:

@ThreadSafe
public class BetterVector <E> extends Vector<E> {
    // When extending a serializable class, you should redefine serialVersionUID
    static final long serialVersionUID = -3963416950630760754L;

    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !contains(x);
        if (absent)
            add(x);
        return absent;
    }
}

不过组合(这里只指原功能的线程安全委托给子组件)的话,需要一些其它的操作,不能直接在方法上同步。因为Q1操作无法保证helper类封装的list的其它方法和putif..的同步问题。

@NotThreadSafe//Q1操作,不安全
class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
    ...
}

@ThreadSafe
class GoodListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    }
    ...
}

还有另一种组合方式,就是将所组合的对象中所有存在风险的方法都加上内部锁,而不用依赖子组件对象是否线程安全。

@ThreadSafe
public class ImprovedList<T> implements List<T> {
    private final List<T> list;

    public ImprovedList(List<T> list) { this.list = list; }

    public synchronized boolean putIfAbsent(T x) {
        boolean contains = list.contains(x);
        if (contains)
            list.add(x);
        return !contains;
    }

    public synchronized boolean add(T e) {
        return list.add(e);
    }

    public synchronized boolean remove(Object o) {
        return list.remove(o);
    }
    .....other alike methods..

写好文档,非常重要!

//待下篇

主要参考自_ Java Concurrency in Practice

猜你喜欢

转载自blog.csdn.net/cysion1989/article/details/77689836
今日推荐