接上一篇《java并发编程(二)对象的共享》
客户端加锁:
java为线程安全提供了一套安全的集合类操作 在java.util.Collections中,在多线程下能 安全的CRUD,但是应该注意,仅是对几何类进行同步,下面看一个实例。
若没有则添加实例:
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 static void main(String[] args) {
ListHelper<String> helper = new ListHelper<String>();
new Thread(new Runnable() {
@Override
public void run() {
helper.list.add("a"); //②
}
}).start();
helper.putIfAbsent("a");
}
}
我们在
①
②
处下断点调试后发现断点1处拿到的锁 是ListHelper 而断点2的锁是 由java虚拟机维护的,不是同一个锁,导致添加和判断不能同步,而在java 的API中也给出例子,要锁list例子
Returns a synchronized (thread-safe) list backed by the specified list. In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list.
It is imperative that the user manually synchronize on the returned list when iterating over it:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
It is imperative that the user manually synchronize on the returned list when iterating over it:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
一定要明确要保证谁是锁,不然加锁知识看似线程安全。
组合:
当为现在的类添加一个原子操作时,有一个种更好的方法:组合。我们不关心源是不是线程安全的,通过继承活着实现,给类添加同步实现线程安全(最好时实现借口,继承需要重写所有方法,并加同步)
public class ArrayListHelper<E> extends ArrayList<E>{