对象的组合《java并发编程实战》

概述

到目前为止,我们已经介绍了关于线程安全与同步的一些基础知识,然而,我们不希望对每一次内存访问都进行分析以确定程序是线程安全的,而是希望将一些现有的线程安全组件组合为更大规模的组件或程序。

设计线程安全的类

package chapter4;

import common.GuardedBy;
import common.ThreadSafe;

/**
 * @author zhen
 * @Date 2018/10/26 11:39
 */
@ThreadSafe
public final class Counter {

    @GuardedBy("this") private long value = 0;

    public synchronized long getValue() {
        return value;
    }

    public synchronized long increment() {
        if (value == Long.MAX_VALUE) {
            throw new IllegalStateException("counter overflow");
        }
        return ++value;
    }
}
使用监视器模式的线程安全计数器例子
通过使用封装技术,可以使得在不对整个程序进行分析的情况下就可以判断一个类是否是线程安全的。
在设计线程安全类的过程中,需要包含以下三个基本要素:
找出构成对象状态的所有变量;
找出约束状态变量的不可变条件;
建立对象状态的并发访问管理策略
1>收集同步需求
如果不了解对象的不变性条件和后验条件,那么就不能确保线程安全性。要满足在状态变量的有效值或状态转换上的各种约束条件,需要借助原子性和封装性
2>依赖状态的操作
类的不变性条件与后验条件约束了再对象上有哪些状态和状态转换是有效的。在某些对象的方法中还包含一些基于状态的先验条件。
在java中,等待某个条件为真的各种内置机制(包括等待和通知机制)都与内置加锁机制紧密关联,要正确使用它们并不容易。想要实现某个等待先验条件为真时才执行的操作,一种更简单的方法是通过现有库中的类(例如阻塞队列Blocking Queue或者信号量Semaphore)来实现依赖状态的行为。
3>状态的所有权
垃圾回收机制使得避免了如何处理所有权的问题
对象封装它拥有的状态反之,对它封装的状态拥有所有权。如果发布了某个可变对象的引用,共享控制权。容器类通常表现出所有权分离的形式。

实例封闭

    将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁
封闭机制更易与构造线程安全的类,因为当封闭类的状态时,在分析类的线程安全性检查时就无须检查整个程序
@ThreadSafe
public class PersonSet {

    @GuardedBy("this")
    private final Set<Person> mySet = new HashSet<>();

    public synchronized void addPerson(Person p) {
        mySet.add(p);
    }

    public synchronized boolean containsPerson(Person p) {
        return mySet.contains(p);
    }

}
通过封闭机制确保线程安全例子
  1>线程监视器模式:
遵循java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护
public class PrivateLock {
    private final Object myLock = new Object();
    @GuardedBy("myLock") Widget widget;

    void someMethod() {
        synchronized (myLock) {
            //访问或者修改Widget的状态
        }
    }
}
通过私有锁保护对象线程安全例子

 

线程安全性的委托

    如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量
如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量

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

    修改原始类
拓展这个类
将拓展代码放入一个辅助类中
1、客户端加锁机制
2、组合
/**
 * @author zhen
 * @Date 2018/10/31 15:08
 * 拓展Vector并增加一个若没有则添加方法
 */
@ThreadSafe
public class BetterVector<E> extends Vector<E> {


    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !contains(x);
        if (absent) {
            add(x);
        }
        return absent;
    }
}
拓展Vector增加一个若没有则添加例子
/**
 * @author zhen
 * @Date 2018/10/31 15:24
 * 非线程安全的“若没有则添加”
 */
@NotThreadSafe
public class ListHelper<E> {

    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    //这里的锁时ListHelper对象,虽然list使用synchronized保证同步,但是锁不一样
    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent) {
            list.add(x);
        }
        return absent;
    }
}
非线程安全的若没有则添加例子
/**
 * @author zhen
 * @Date 2018/10/31 15:32
 *  通过客户端加锁来实现“若没有则添加”
 */
@ThreadSafe
public class ListHelper1<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;
        }
    }
}
通过客户端加锁实现若没有则添加例子
/**
 * @author zhen
 * @Date 2018/10/31 15:40
 * 通过组合实现若没有则添加
 */
@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;
    }

    @Override
    public int size() {
        return list.size();
    }

    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return list.contains(o);
    }

    @Override
    public Iterator<T> iterator() {
        return list.iterator();
    }

    @Override
    public Object[] toArray() {
        return list.toArray();
    }

    @Override
    public <T1> T1[] toArray(T1[] a) {
        return list.toArray(a);
    }

    @Override
    public boolean add(T t) {
        return list.add(t);
    }

    @Override
    public boolean remove(Object o) {
        return list.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return list.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        return list.addAll(c);
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        return list.addAll(index, c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return list.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return list.retainAll(c);
    }

    @Override
    public void clear() {
        list.clear();
    }

    @Override
    public T get(int index) {
        return list.get(index);
    }

    @Override
    public T set(int index, T element) {
        return list.set(index, element);
    }

    @Override
    public void add(int index, T element) {
        list.add(index, element);
    }

    @Override
    public T remove(int index) {
        return list.remove(index);
    }

    @Override
    public int indexOf(Object o) {
        return list.indexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
        return list.lastIndexOf(o);
    }

    @Override
    public ListIterator<T> listIterator() {
        return list.listIterator();
    }

    @Override
    public ListIterator<T> listIterator(int index) {
        return list.listIterator(index);
    }

    @Override
    public List<T> subList(int fromIndex, int toIndex) {
        return list.subList(fromIndex,toIndex);
    }
}
推荐:通过组合的方式实现若没有则添加例子

 

将同步策略文档化

在文档中说明客户代码需要了解的线程安全保证以及代码维护人员需要了解的同步策略。
posted @ 2018-11-06 16:00  guodaxia  阅读(180)  评论(0)    收藏  举报