文档章节

concurrency - Avoid excessive synchronization

why_Dk37
 why_Dk37
发布于 2016/12/11 16:47
字数 1347
阅读 5
收藏 0
点赞 0
评论 0

avoid excessive synchronization 避免过度使用synchronization

Here is an example,which implements an observable set wrapper.It allow clients to subscribe to notifications when elements are added to the set.this is the Observer pattern. for brevity`s, the class does not provide notifications when elements are removed from the set.

这有一个观察着封装的set,它允许客户端订阅添加元素的通知,为了简介,没有提供移出元素的通知。

Observers subscribe to notifications by invoking the addObserver method and unsubscribe by invoking the removeObserver method. In both cases, an instance of the callback interface is passed to the method.

观察者订阅调用addObserver,取消订阅调用removeObserver,两种操作的参数都回调接口。


import java.util.*;

/**
 * Created by why on 10/23/2016.
 */
public class ObservableSet0<E> extends HashSet<E> {
    public ObservableSet0(Set<E> set) {
        super(set);
    }

    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);
        }
    }

    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;
    }

    public static void main(String[] args) {
        ObservableSet0<Integer> set = new ObservableSet0<Integer>(new HashSet<Integer>());
        set.addObserver(new SetObserver<Integer>() {
            public void added(ObservableSet0<Integer> s, Integer e) {
                System.out.println(e);
            }
        });
        for (int i = 0; i < 100; i++)
            set.add(i);
    }

    static class SetObserver<E> {
        public void added(ObservableSet0<E> es, E element) {
        }
    }
}

The result will print 0 through 99.Now let`s try something els.Suppose we replace the addObserver call with one that passes an observer that prints the Integer value that was added to the set and removes itself if the value is 23.

结果是打印0-99。我们试着做一些其他事,添加一个订阅者,打印数字,并在数字是23的时候移出自己。

import java.util.*;

/**
 * caes an  java.util.ConcurrentModificationException
 * Changing ArrayList to CopyOnWriteArrayList can solve this problem
 * <p>
 * Created by why on 10/23/2016.
 */
public class ObservableSet1<E> extends HashSet<E> {
    public ObservableSet1(Set<E> set) {
        super(set);
    }

    // fixme 2016-10-23 CopyOnWriteArrayList can solve this problem
    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);
        }
    }

    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;
    }

    public static void main(String[] args) {
        ObservableSet1<Integer> set = new ObservableSet1<Integer>(new HashSet<Integer>());
        set.addObserver(new SetObserver<Integer>() {
            public void added(ObservableSet1<Integer> s, Integer e) {
                System.out.println(e);
                if (e == 23) {
                    set.removeObserver(this);
                }
            }
        });
        for (int i = 0; i < 100; i++)
            set.add(i);
    }

    static class SetObserver<E> {
        public void added(ObservableSet1<E> es, E element) {
        }
    }
}

You might expect the program to print the numbers 0 through 23.But you get an unexpected ConcurrentModificationException. The problem is that notifyElementAdded is in the process of iterating over the observers list when it invokes the observer`s added method.he iteration in the notifyElementAddded method is in a synchronized block to prevent concurrent modification, but is does not prevent the iterating thread itself from calling back into the observable set and modifying its observers list.

Now let`s try something odd:Write an observer that attempts to unsubscribe, but instead of calling removeObserver directly, it engages the services of another thread to do the deed.This observer uses an executor service.


import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * dead lock
 * <p>
 * This time we don’t get an exception; we get a deadlock. The background thread
 * calls s.removeObserver, which attempts to lock observers, but it can’t acquire
 * the lock, because the main thread already has the lock. All the while, the main
 * thread is waiting for the background thread to finish removing the observer, which
 * explains the deadlock.
 * Created by why on 10/23/2016.
 */
public class ObservableSet2<E> extends HashSet<E> {
    public ObservableSet2(Set<E> set) {
        super(set);
    }

    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);
        }
    }

    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;
    }

    public static void main(String[] args) {
        ObservableSet2<Integer> set = new ObservableSet2<Integer>(new HashSet<Integer>());
        set.addObserver(new SetObserver<Integer>() {
            public void added(ObservableSet2<Integer> s, Integer e) {
                System.out.println(e);
                if (e == 23) {
                    ExecutorService executor =
                            Executors.newSingleThreadExecutor();
                    final SetObserver<Integer> observer = this;
                    try {
                        executor.submit(new Runnable() {
                            public void run() {
                                s.removeObserver(observer);
                            }
                        }).get();
                    } catch (ExecutionException ex) {
                        throw new AssertionError(ex.getCause());
                    } catch (InterruptedException ex) {
                        throw new AssertionError(ex.getCause());
                    } finally {
                        executor.shutdown();
                    }
                }
            }
        });
        for (int i = 0; i < 100; i++)
            set.add(i);
    }

    static class SetObserver<E> {
        public void added(ObservableSet2<E> es, E element) {
        }
    }
}

This time we don`t get an exception,we get a deadlock.The background thread calls s.removeObserve,which attempts to lock observers,bug it can not acquire the lock,because the main thread already has the lock.All the while,the main thread is waiting for the background thread to finish removing the observer,which explains the deadlock.

这是一个人为的例子,因为我们没有必要再观察者中时候线程池。

解决上面两种情况引起的问题,我们可以把方法调用移出同步块。就上面的例子,我们可以在notifyElementAdded方法中生产observer的快照,然后可以 安全的去掉锁。这样上面两个例子都会完美运行。

private void notifyElementAdded(E element) {
    List<SetObserver<E>> snapshot = null;
    synchronized(observers) {
        snapshot = new ArrayList<SetObserver<E>>(observers);
    }
    for (SetObserver<E> observer : snapshot)
        observer.added(this, element);
}

事实上有更好的方法,java 1.5提供的concurrent collection中的CopyOnWriteArrayList,就是为这个目的而生。当元素改变时,他会新复制一份 完整的数据,对其他情况而言这种操作可能非常粗暴,但是非常适合保存observer,因为他改变的情况比较少。

private final List<SetObserver<E>> observers = new CopyOnWriteArrayList<SetObserver<E>>();
public void addObserver(SetObserver<E> observer) {
    observers.add(observer);
}
public boolean removeObserver(SetObserver<E> observer) {
    return observers.remove(observer);
}
private void notifyElementAdded(E element) {
    for (SetObserver<E> observer : observers)
        observer.added(this, element);
}

所有的外部调用都在同步块之外,也就是开放式调用(open call)。除了避免故障的产生,还可以大大的提高并发。

在同步块中尽可能的少操作

如果需要进行大量操作,可以考虑把这些操作移出同步块。

![My helpful screenshot]({{ site.url }}/assets/screenshot.jpg)

© 著作权归作者所有

共有 人打赏支持
why_Dk37
粉丝 1
博文 22
码字总数 17584
作品 0
海淀
程序员
ThreadLocal in Java - Example Program and Tutorial

ThreadLocal in Java is another way to achieve thread-safety apart from writing immutable classes. If you have been writing multi-threaded or concurrent code in Java then you mus......

perfectspr ⋅ 2014/12/10 ⋅ 0

Intrinsic Locks and Synchronization

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. (The API specification often refers to this entity simply as a "monitor.") Intrin......

恋空御月 ⋅ 2016/08/19 ⋅ 0

concurrency 同步

concurrency 同步 avoid excessive synchronization 避免过度使用synchronization Here is an example,which implements an observable set wrapper.It allow clients tosubscribe to notif......

why_Dk37 ⋅ 2016/10/23 ⋅ 0

GTK+ 3.8.0 发布,支持 Wayland 1.0

GTK+ 3.8.0 发布了,该版本支持 Wayland 1.0,此外还包含很多新特性和性能方面的提升。 包括: Wayland: - we support Wayland 1.0 now Broadway: - we install a separate server: broadway...

oschina ⋅ 2013/03/26 ⋅ 9

Concurrency In Golang

Yesterday, I answered a question in Quora about the concurrency model in Go. Now, I feel like I want to say more!! Concurrency in Golang is one of the most powerful features in ......

LsDimplex ⋅ 2015/12/08 ⋅ 0

java 的HashMap高并发问题

今天不知为什么服务器卡死了 把所有线程的堆栈打印出来是这样的 java.util.HashMap.get .................. com.labox.common.net.ReceiveWorker.run java.util.concurrent.ThreadPoolExecu...

JavaGG ⋅ 2009/08/25 ⋅ 9

JVM垃圾回收调优【官方版】

Contents Title and Copyright Information Preface Audience Documentation Accessibility Related Documents Conventions 1 Introduction 2 Ergonomics Garbage Collector, Heap, and Runt......

nj-zhangmq ⋅ 2016/12/22 ⋅ 0

How to: Use SpinLock for Low-Level Synchronization

跑了一下下面的例子,发现在100000级别,SpinLock比对象lock要快不少,但是随着N的增大,效率差异基本没有,有时甚至更加慢。 In this example, the critical section performs a minimal amo...

pczhangtl ⋅ 2013/12/05 ⋅ 0

小型Java虚拟机--Avian

Avian 是一个轻量级的 Java 虚拟机和类库,提供了 Java 特性的一个有用的子集,适合开发跨平台、自包容的应用程序。 Avian 实现非常快速而且体积小,主要特性: Just-In-Time (JIT) 编译,快...

匿名 ⋅ 2011/08/09 ⋅ 0

2013-05-19 MapDB 0.9.2 已经发布

该版本主要进行了bug修复和性能的提升改进并且介绍了数据提取功能 Version 0.9.2 (2013-05-19) CRITICAL upgrade urgency. This release fixes some critical bugs. It also improves perfo...

恺哥 ⋅ 2013/05/31 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Sqoop

1.Sqoop: 《=》 SQL to Hadoop 背景 1)场景:数据在RDBMS中,我们如何使用Hive或者Hadoop来进行数据分析呢? 1) RDBMS ==> Hadoop(广义) 2) Hadoop ==> RDBMS 2)原来可以通过MapReduce I...

GordonNemo ⋅ 45分钟前 ⋅ 0

全量构建和增量构建的区别

1.全量构建每次更新时都需要更新整个数据集,增量构建只对需要更新的时间范围进行更新,所以计算量会较小。 2.全量构建查询时不需要合并不同Segment,增量构建查询时需要合并不同Segment的结...

无精疯 ⋅ 56分钟前 ⋅ 0

如何将S/4HANA系统存储的图片文件用Java程序保存到本地

我在S/4HANA的事务码MM02里为Material维护图片文件作为附件: 通过如下简单的ABAP代码即可将图片文件的二进制内容读取出来: REPORT zgos_api.DATA ls_appl_object TYPE gos_s_obj.DA...

JerryWang_SAP ⋅ 今天 ⋅ 0

云计算的选择悖论如何对待?

导读 人们都希望在工作和生活中有所选择。但心理学家的调查研究表明,在多种选项中进行选择并不一定会使人们更快乐,甚至不会产生更好的决策。心理学家Barry Schwartz称之为“选择悖论”。云...

问题终结者 ⋅ 今天 ⋅ 0

637. Average of Levels in Binary Tree - LeetCode

Question 637. Average of Levels in Binary Tree Solution 思路:定义一个map,层数作为key,value保存每层的元素个数和所有元素的和,遍历这个树,把map里面填值,遍历结束后,再遍历这个map,把每...

yysue ⋅ 今天 ⋅ 0

IDEA配置和使用

版本控制 svn IDEA版本控制工具不能使用 VCS-->Enable Version Control Integration File-->Settings-->Plugins 搜索Subversion,勾选SVN和Git插件 删除.idea文件夹重新生成项目 安装SVN客户......

bithup ⋅ 今天 ⋅ 0

PE格式第三讲扩展,VA,RVA,FA的概念

作者:IBinary 出处:http://www.cnblogs.com/iBinary/ 版权所有,欢迎保留原文链接进行转载:) 一丶VA概念 VA (virtual Address) 虚拟地址的意思 ,比如随便打开一个PE,找下它的虚拟地址 这边...

simpower ⋅ 今天 ⋅ 0

180623-SpringBoot之logback配置文件

SpringBoot配置logback 项目的日志配置属于比较常见的case了,之前接触和使用的都是Spring结合xml的方式,引入几个依赖,然后写个 logback.xml 配置文件即可,那么在SpringBoot中可以怎么做?...

小灰灰Blog ⋅ 今天 ⋅ 0

冒泡排序

原理:比较两个相邻的元素,将值大的元素交换至右端。 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第...

人觉非常君 ⋅ 今天 ⋅ 0

Vagrant setup

安装软件 brew cask install virtualboxbrew cask install vagrant 创建project mkdir -p mst/vmcd mst/vmvagrant init hashicorp/precise64vagrant up hashicorp/precise64是一个box......

遥借东风 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部