首页 > 代码库 > 【Effective Java】12、避免过度同步
【Effective Java】12、避免过度同步
这里有一个辅助基础类
package cn.xf.cp.ch02.item16;import java.util.Collection;import java.util.Iterator;import java.util.Set;public class ForwardingSet<E> implements Set<E>{ /** * 这个类作为转发类,内部通过复合的方式把set作为一个组件 */ private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } @Override public int size() { return s.size(); } @Override public boolean isEmpty() { return s.isEmpty(); } @Override public boolean contains(Object o) { return s.contains(o); } @Override public Iterator<E> iterator() { return s.iterator(); } @Override public Object[] toArray() { return s.toArray(); } @Override public <T> T[] toArray(T[] a) { return s.toArray(a); } @Override public boolean add(E e) { return s.add(e); } @Override public boolean remove(Object o) { return s.remove(o); } @Override public boolean containsAll(Collection<?> c) { return s.containsAll(c); } @Override public boolean addAll(Collection<? extends E> c) { return s.addAll(c); } @Override public boolean retainAll(Collection<?> c) { return s.retainAll(c); } @Override public boolean removeAll(Collection<?> c) { return s.removeAll(c); } @Override public void clear() { s.clear(); }}
然后使用一个类使这个类可以被观察者注册
package cn.xf.cp.ch02.item67;import java.util.ArrayList;import java.util.Collection;import java.util.List;import java.util.Set;import cn.xf.cp.ch02.item16.ForwardingSet;public class ObservableSet<E> extends ForwardingSet<E>{ public ObservableSet(Set<E> s) { super(s); } private final List<SetObserver<E>> observers = new ArrayList<SetObserver<E>>(); public void addObserver(SetObserver<E> observer) { synchronized (observers) { observers.add(observer); } } public boolean removeObserver(SetObserver<E> observer) { synchronized (observers) { return observers.remove(observer); } } // This method is the culprit private void notifyElementAdded(E element) { synchronized (observers) { for (SetObserver<E> observer : observers) observer.added(this, element); } } private void notifyElementAdded2(E element) { List<SetObserver<E>> snapshot = null; synchronized (observers) { //这里拍一个快照,这样我们遍历的时候就不用对原来的集合进行上锁了 snapshot = new ArrayList<SetObserver<E>>(observers); } for (SetObserver<E> observer : snapshot) observer.added(this, element); } @Override public boolean add(E e) { //调用父类函数添加到集合中 boolean added = super.add(e); if(added) { //添加成功,观察者保存注册对象 notifyElementAdded(e); } return added; } @Override public boolean addAll(Collection<? extends E> c) { boolean result = false; for(E element : c) { //做或运算,只要有一个add添加成功,那么result就是true result |= add(element); } return result; }}
可以注意到,这个类中已经有一个地方有两个方法,这个后面会将
观察者:
package cn.xf.cp.ch02.item67;public interface SetObserver<E>{ /** * 当一个元素添加到ObservableSet对象中的时候,调用 * @param set * @param element */ void added(ObservableSet<E> set, E element);}
测试:
package cn.xf.cp.ch02.item67;import java.util.HashSet;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test{ @org.junit.Test public void test() { //创建一个被观察的对象 ObservableSet<Integer> set = new ObservableSet<Integer>(new HashSet<Integer>()); //添加一个观察者 set.addObserver(new SetObserver<Integer>() { public void added(ObservableSet<Integer> s, Integer e) { System.out.println(e); } }); for (int i = 0; i < 100; i++) set.add(i); } @org.junit.Test public void test2() { ObservableSet<Integer> set = new ObservableSet<Integer>(new HashSet<Integer>()); set.addObserver(new SetObserver<Integer>() { public void added(ObservableSet<Integer> s, Integer e) { System.out.println(e); if (e == 23) //到23,我们取消这个观察者,但是会爆出异常,因为在迭代遍历列表的时候我们自己修改了列表,这是非法的 s.removeObserver(this); } }); for (int i = 0; i < 100; i++) set.add(i); } /** * 那么如何取消观察者者呢???? * 我们使用另外的一个线程在23的时候删除这个观察者 */ @org.junit.Test public void test3() { ObservableSet<Integer> set = new ObservableSet<Integer>(new HashSet<Integer>()); set.addObserver(new SetObserver<Integer>() { @Override public void added(final ObservableSet<Integer> set, Integer element) { System.out.println(element); //如果是23 if(element == 23) { //线程池,创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务 ExecutorService executor = Executors.newSingleThreadExecutor(); final SetObserver<Integer> observer = this; try { executor.submit(new Runnable() { @Override public void run() { //这里会死锁 set.removeObserver(observer); } }).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } } }); for (int i = 0; i < 100; i++) set.add(i); }}
结果:
测试2
这里会抛出异常,为什么,理由已经在类中写了
测试3
这个会造成死锁,要使用
让我们外部调用的内部集合避免上锁,而是建立一个快照,然后对快照进行上锁
要点:就是外部调用函数,最好不要进入到同步区,简而言之同步区尽量就在内部进行操作,同步区尽可能少的工作,获得锁,处理数据,释放锁,都在内部完成
【Effective Java】12、避免过度同步
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。