代码1:
[code="java"]@NotThreadSafe
class BadListHelper {
public List list = Collections.synchronizedList(new ArrayList());
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
@ThreadSafe
public class ImprovedList implements List {
private final List list;
/**
* PRE: list argument is thread-safe.
*/
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;
}
}
[/code]
代码1不是线程安全的,而代码二是线程安全的 (最上面的是代码1,最后的代码是代码我)
二者的区别主要是final 修饰了list ,而final 如果指向引用的话,引用不可变,而所指向的引用是可以变化的
[b]
为什么代码1不是线程安全的,而代码2是线程安全的[/b]
看看JDK文档:
JDK API 文档如下
(http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html)
[b]API文档给出的例子[/b]
[code="java"] List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list) {
// Must be in synchronized block
。。。。。。
}
[/code]
这样的list就只能这样使用才能到达线程安全的目的。
第一个:
JDK API 文档如下(http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html)
[code="java"] List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list) {
// Must be in synchronized block
。。。。。。
}
[/code]
第二个:
[quote]synchronized 方法控制对类成员变量的访问:每个类实例对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例对象,其所有声明为 synchronized 的成员方法中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对象对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问
[/quote]
sychronized方法执行时获取的是对象的锁后,无法修改对对象的成员,直到方法执行完。
所以说在执行putIfAbsent方法时,其他线程无法获取到对象的锁,更无法获取到实例成员list
[quote]代码2 也就用了synchronized 只不过是它的list 是 final
也就说是list不能改,但 list里的元素还可以修改
为什么代码2就线程安全了呢 [/quote]
你说的没错。
但是你想想这样想想:对象被锁定之后,不可能获取到对象里面的元素了。其他线程就不能使用这个对象。
[quote]为什么我这问答,没有在提问在前面[/quote]