JAVA并发-为现有的线程安全类添加原子方法

JAVA中有许多线程安全的基础模块类,一般情况下,这些基础模块类能满足我们需要的所有操作,但更多时候,他们并不能满足我们所有的需要。此时,我们需要想办法在不破坏已有的线程安全类的基础上添加一个新的原子操作。有如下4中方案:
1 修改类的源码,以添加新的原子操作
2 继承该线程安全类,并添加原子操作
3 使用客户端加锁方式
4 使用组合方式 (推荐)
一般来讲,修改源码的方式不太可行,这样会破坏原有类的封装性而且有些时候,源码不可获得。我们从第二种方式开始举例:

假设现在对于类Vector,我们知道它是线程安全类。如果想为他添加一个“若没有则添加”方法,可是如下进行:
public class ImprovedVector<T> extends Vector<T>{
    public synchronized boolean putIfAbsent(T x){
        boolean flag=contains(x);
        if(!flag)
             add(x);
        return !flag;
    }
}

我们来分析上面的代码:使用ImprovedVector类对象的内置锁,保证了contains()和add()方法的原子性,由于ImprovedVector类对象的内置锁也就是Vector类对象的内置锁(即add()方法和contains()方法的锁),这样有保证了add()方法和contains()方法的可见性,可以达到预期效果。

第三种方法举例:
(错误的实现)
public class ImprovedList<T>{
    public List<T> list=Collections.synchronizedList(new ArrayList<T>());
    public synchronized boolean putIfAbsent(T x){
        boolean flag=list.contains(x);
        if(!flag)
             list.add(x);
        return !flag;
    }
}

上面的例子是一个错误的例子,我们来分析下:首先,synchronized保证了list.contains()方法和list.add()方法的原子性,假设现在有一个类对象在执行putIfAbsent()方法,而且即将执行(还没执行)list.add(2)方法,此时,有另外一个线程抢先执行了list.add(2)方法,该线程执行完毕之后,释放了list的锁,接着即将执行(还没执行)list.add(2)方法开始得到CPU并执行。瞧,这个过程中,数字2被添加了2次。就是说,上面的代码中仅仅保证了contains()方法和add()方法的原子性,以及对对list引用操作的互斥性,并没有保证list.add()方法的可见性。
仔细想想,问题出在putIfAbsent()方法的锁与list对象的锁不是同一个,putIfAbsent()方法的锁是ImprovedList类的锁,而list.add()方法的锁是Collections.synchronizedList()使用的锁,因此将上面的代码改成:
(正确的实现)
public class ImprovedList<T>{
    public List<T> list=Collections.synchronizedList(new ArrayList<T>());
    public synchronized boolean putIfAbsent(T x){
            synchronized(list){
            boolean flag=list.contains(x);
            if(!flag)
                 list.add(x);
            return !flag;
        }
    }
}


第四种方法举例:
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 flag=list.contains(x);
            if(!flag)
                 list.add(x);
            return !flag;
        }
    }
    ...实现List<T>接口中的其他方法
}

乍一看发现,上面的代码在安全性方面好像弱了好多, putIfAbsent(T x)方法中的fianl变量list可能连线程安全类都不是,但是对于上面的代码,我们有个假设(当某个链表对象在传递给ImprovedList的构造函数之后,客户代码再也不会使用这个对象,而是使用与其对应的ImprovedList对象),有了这个假设前提,上面的代码就是线程安全的了。

上面就是JAVA并发编程中,在一个已有的线程安全类的基础上添加同步函数的4个方法。


猜你喜欢

转载自yizhenn.iteye.com/blog/2287582