并发-对象的组合


实例封闭

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

  • java类库中有很多线程封闭示例: Collections.synchronizedList及其类似方法,这些工厂方法通过装饰器(Decorator)设计模式将容器类封装在一个同步的包装容器对象中
//通过封闭机制来确保线程安全
public class PersonSet {
    //将数据封装在对象内部
    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);
    }

    interface Person {
    }
}
  1. 监视器模式

    遵循java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护,例如Vector和HashTable

//监视器模式: 通过一个私有锁来保护状态
public class PrivateLock {
    private final Object myLock = new Object();
    Widget widget; //要封装的对象widget

    void someMethod() {
        synchronized (myLock) { //由内置锁(监视器)保护
            // 访问或修改widget对象状态
        }
    }
}
  1. 线程安全性的委托

    如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量

//将线程安全性委托给多个状态变量
public class VisualComponent {
    //两个事件监听器之间不存在任何关联,将VisualComponent类的线程安全性委托给这两个线程安全的监听器列表
    //由CopyOnWriteArrayList管理保证线程安全性
    private final List<KeyListener> keyListeners = new CopyOnWriteArrayList<KeyListener>();
    private final List<MouseListener> mouseListeners = new CopyOnWriteArrayList<MouseListener>();

    public void addKeyListener(KeyListener listener) {
        keyListeners.add(listener);
    }
    public void addMouseListener(MouseListener listener) {
        mouseListeners.add(listener);
    }
    public void removeKeyListener(KeyListener listener) {
        keyListeners.remove(listener);
    }
    public void removeMouseListener(MouseListener listener) {
        mouseListeners.remove(listener);
    }
}

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

  1. 扩展类
//没有则添加: 继承然后扩展类
public class BetterVector <E> extends Vector<E> {
    //对于一个继承了serializable的类,应该重新定义其serialVersionUID
    static final long serialVersionUID = -3963416950630760754L;
    //不存在时添加: 增加一个原子方法(使用同步锁)
    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !contains(x);
        if (absent)
            add(x);
        return absent;
    }
}
  1. 客户端加锁机制
//没有则添加: 通过客户端加锁方式实现
class GoodListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
        synchronized (list) { //锁对象必须和list对象内部方法使用的锁对象为同一个
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    }
}
  1. 组合
//没有则添加: 通过组合实现(该方式更加健壮)
//实际上就是java监视器模式
public class ImprovedList<T> implements List<T> {
    //维护一个list对象,将实现委托给list对象的方法
    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;
    }

    //方法实现委托给list对象的方法,其他方法使用同样方式即可
    public synchronized boolean add(T e) {
        return list.add(e);
    }
    public synchronized boolean remove(Object o) {
        return list.remove(o);
    }
}

文章作者: Bryson
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bryson !
评论
 上一篇
并发-构建基础模块 并发-构建基础模块
同步容器类 Vector , Hashtable , Collections.synchronizedXxx等工厂方法创建 这些类实现线程安全方式: 将它们的状态封装起来,并对每个共有方法都进行同步,是的每次只有一个线程能方法容器状态
2019-08-20
下一篇 
并发-对象的共享 并发-对象的共享
非原子的64操作 非volatile类型的64位数值变量(double和long),JVM允许将64位的读或写操作分解为两个32位操作,因此在多线程中使用共享且可变的long与double类型的变量是不安全的,需要使用volatile声明或
2019-08-10
  目录