看一下下面这个类,他是不是线程安全的?一眼看过去,没毛病啊,肯定安全啊。list是安全的,ListHelp中的方法是安全的,所以这个类是线程安全的。其实,这边是给你制造了一个假象,这个类并不是线程安全的,原因就是方法1的同步方法中对list进行先检查后执行的操作(一般都要求先检查后执行的操作是原子性的),但是list对象并不是你这个方法所私有的,看着list是public的,也就是说,list是发布出去的,其他地方可以修改这个list。而在方法1中执行完list.contain(x)后,其他方法可以对list进行添加或者删除元素操作,导致方法1中的absent结果会失效,当你根据失效的absent结果去进行操作时,必然导致数据上不一致的问题。那么为什么这个类是不安全的呢,因为方法1获得了ListHelp的锁并没有获得list的锁,导致,方法1中对list的先检查后执行操作不是满足原子性的。那么后面的代码即是最终完成该类是线程安全类的答案。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ListHelp{
public Listlist = Collections.synchronizedList(new ArrayList());
// 方法1
public synchronized boolean putIfAbsent(E x) { // 这个方法并不是线程安全的,因为当前只获得了ListHelp的锁,并没有获得list的锁,没有办法保证当前操作的原子性
boolean absent = !list.contains(x);
if (absent) {
list.add(x);
}
return absent;
}
}
线程安全类:方法2直接锁住了ListHelp和list。所以整个先检查后执行操作是原子性的。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ListHelp{
public Listlist = Collections.synchronizedList(new ArrayList());
// 方法2
public synchronized boolean putIfAbsentSafe(E x) { // 这个方法是线程安全的
synchronized(list ) {
boolean absent = !list.contains(x);
if (absent) {
list.add(x);
}
return absent;
}
}
}