Effective Java - 避免过度同步

第79条 避免过度同步

  1. 在一个被同步的区域内部,不要调用设计成要被覆盖的方法,或者是由客户端以函数对象的形式提供的方法

  2. 死锁的例子:

    public class ObservableSet<E> extends ForwardingSet<E> {
        public ObservableSet(Set<E> set) { super(set); }
        private final List<SetObserver<E>> observers = new ArrayList<>();
        public void addObserver(SetObserver<E> observer) {
            synchronized(observers) {
                observers.add(observer);
            }
        }
        public boolean removeObserver(SetObserver<E> observer) {
            synchronized(observers) {
                return observers.remove(observer);
            }
        }
        private void notifyElementAdded(E element) {
            synchronized(observers) {
                for (SetObserver<E> observer : observers)
                    observer.added(this, element);
            }
        }
        @Override public boolean add(E element) {
            boolean added = super.add(element);
            if (added)
                notifyElementAdded(element);
            return added;
        }
        @Override public boolean addAll(Collection<? extends E> c) {
            boolean result = false;
            for (E element : c)
                result |= add(element); // Calls notifyElementAdded
            return result;
        }
    }
    
    set.addObserver(new SetObserver<>() {
        public void added(ObservableSet<Integer> s, Integer e) {
            System.out.println(e);
            if (e == 23) {
                ExecutorService exec = Executors.newSingleThreadExecutor();
                try {
                    exec.submit(() -> s.removeObserver(this)).get();
                } catch (ExecutionException | InterruptedException ex) {
                    throw new AssertionError(ex);
                } finally {
                    exec.shutdown();
                }
            }
        }
    });
    

    这时候优于执行到这里的时候会去原list里面remove,而这时候原list已经被锁了,就会造成死锁

  3. 由于 Java 程序设计语言中的锁是可重入的,所以两次进入同一个锁的同步代码块调用不会死锁

  4. CopyOnWriteArrayList过重新拷贝整个底层数组,进而实现所有的修改操作

  5. 应该在同步区域内做尽可能少的工作

思考

  1. 死锁是一定需要注意的,它不会抛出任何异常,只会让所有的线程都处于阻塞状态,后续发现和排查问题都比较困难
  2. CopyOnWriteArrayList虽然可以避免很多多线程问题,但是在对于写操作特别频繁的场景下,CopyOnWriteArrayList的性能会下滑的非常严重,如果写操作非常多,使用CopyOnWriteArrayList需要慎重考虑
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • ITEM 79: AVOID EXCESSIVE SYNCHRONIZATION  item 78 警告了同步不足...
    rabbittttt阅读 368评论 0 0
  • 对象的创建与销毁 Item 1: 使用static工厂方法,而不是构造函数创建对象:仅仅是创建对象的方法,并非Fa...
    孙小磊阅读 2,040评论 0 3
  •   项目78警告同步不足的危险。这一项涉及相反的问题。根据不同的情况,过度的同步可能导致性能下降、死锁,甚至不确定...
    难以置信的优雅阅读 222评论 0 0
  • Java 语言支持的类型分为两类:基本类型和引用类型。整型(byte 1, short 2, int 4, lon...
    xiaogmail阅读 1,370评论 0 10
  • 一.线程安全性 线程安全是建立在对于对象状态访问操作进行管理,特别是对共享的与可变的状态的访问 解释下上面的话: ...
    黄大大吃不胖阅读 878评论 0 3