日期:2014-05-20  浏览次数:20786 次

哪个是线程安全的
public class ListHelper<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;
        }
    }
}

---------------------------------------------------------------------------------------------

public class ListHelper<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;
            }
        }        
    }
}


这两个代码,不同的地方是synchronized一个是修饰了方法,一个是修饰了list,目的都是让list线程安全,书上写的上面的写法不是线程安全的,下面的是线程安全的,线程安全的解释我可以理解,但上面的写法为什么不是线程安全的?
------解决方案--------------------
up up up up up up up 
------解决方案--------------------
up up up up up
------解决方案--------------------
synchronized修饰方法是获得本对象的锁,而不是list
在boolean absent = !list.contains(x);和list.add(x);之间,list就有可能会被别的对象改变(比如通过反射),导致线程不安全
用synchronized(list)的话就不会了
------解决方案--------------------
从操作角度来看,用了Collections.synchronizedList,不会出现数据错误,数组越界之类

从逻辑绝度来看,下面的也不见得安全,用list做锁,其他没synchronized(list)的代码块可以直接操作list
而contains 和 add两个动作中间有间隙,其他操作非synchronized(list)可以从中间插入
假设类中其他代码块操作list的时候都有synchronized(list),可以认为是安全的
但同样道理,类中其他操作list的方法全部用synchronized修饰其实也很安全


------解决方案--------------------
Collections操作集合的对象,源代码中也是将对象(要进行加锁)的对象都设置为final的,一般都这样.一般不会把普通的对象进行lock,要么就用类字节码对象lock.
------解决方案--------------------
我给你解释下吧。
首先的你的List已经是线程安全的了(public List<E> list = Collections.synchronizedList(new ArrayList<E>());具体可参见API),但是你写了个方法实现无重复添加的功能,上面的方法,对方法加synchronized,假如已经执行到 if 行了,别的地方调用了add,这时boolean absent = !list.contains(x)的判断已经无效了,就出现你说的list不安全了。第二种给List加synchronized就OK,因为你在进入时,别的地方不能调用add
------解决方案--------------------
第一个获得ListHelper 对象的锁,这把锁和list对象明显是不同的锁。进入同步块后到退出前,其它的线程还能调用list.add(),list.remove()方法。
但第二个例子,因为 synchronized(list),这时操作的是list对象的锁,也就是list.add(),list.contains()等方法用的是同一把锁,当进入同步块时,其它的线程不可能在同步块退出前调用list.add(),list.remove()等这些同步方法,所以是线程安全的。
------解决方案--------------------
两种都没有绝对的线程安全。
------解决方案--------------------
引用:
从操作角度来看,用了Collections.synchronizedList,不会出现数据错误,数组越界之类

从逻辑绝度来看,下面的也不见得安全,用list做锁,其他没synchronized(list)的代码块可以直接操作list
而contains 和 add两个动作中间有间隙,其他操作非synchronized(list)可以从中间插入
假设类中其他代码块操作list的时候都有synchronized(list),可以认为是安全的
但同样道理,类中其他操作list的方法全部用synchronized修饰其实也很安全


看了下源码,发觉自己错了。
SynchronizedList的锁就是自己本身,所以第二中的确是能锁住的