`
IXHONG
  • 浏览: 438484 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

同步容器

阅读更多

为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器、并发容器、阻塞队列、Synchronizer(比如CountDownLatch)。今天我们就来讨论下同步容器。

  以下是本文的目录大纲:

  一.为什么会出现同步容器?

  二.Java中的同步容器类

  三.同步容器的缺陷

  若有不正之处请多多谅解,并欢迎批评指正。

  请尊重作者劳动成果,转载请标明原文链接:

  http://www.cnblogs.com/dolphin0520/p/3933404.html

一.为什么会出现同步容器?

  在Java的集合容器框架中,主要有四大类别:List、Set、Queue、Map。

  List、Set、Queue接口分别继承了Collection接口,Map本身是一个接口。

  注意Collection和Map是一个顶层接口,而List、Set、Queue则继承了Collection接口,分别代表数组、集合和队列这三大类容器。

  像ArrayList、LinkedList都是实现了List接口,HashSet实现了Set接口,而Deque(双向队列,允许在队首、队尾进行入队和出队操作)继承了Queue接口,PriorityQueue实现了Queue接口。另外LinkedList(实际上是双向链表)实现了了Deque接口。

  像ArrayList、LinkedList、HashMap这些容器都是非线程安全的。

  如果有多个线程并发地访问这些容器时,就会出现问题。

  因此,在编写程序时,必须要求程序员手动地在任何访问到这些容器的地方进行同步处理,这样导致在使用这些容器的时候非常地不方便。

  所以,Java提供了同步容器供用户使用。

二.Java中的同步容器类

  在Java中,同步容器主要包括2类:

  1)Vector、Stack、HashTable

  2)Collections类中提供的静态工厂方法创建的类

  Vector实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施。

  Stack也是一个同步容器,它的方法也用synchronized进行了同步,它实际上是继承于Vector类。

  HashTable实现了Map接口,它和HashMap很相似,但是HashTable进行了同步处理,而HashMap没有。

  Collections类是一个工具提供类,注意,它和Collection不同,Collection是一个顶层的接口。在Collections类中提供了大量的方法,比如对集合或者容器进行排序、查找等操作。最重要的是,在它里面提供了几个静态工厂方法来创建同步容器类,如下图所示:

  

  

三.同步容器的缺陷

  从同步容器的具体实现源码可知,同步容器中的方法采用了synchronized进行了同步,那么很显然,这必然会影响到执行性能,另外,同步容器就一定是真正地完全线程安全吗?不一定,这个在下面会讲到。

  我们首先来看一下传统的非同步容器和同步容器的性能差异,我们以ArrayList和Vector为例:

1.性能问题

  我们先通过一个例子看一下Vector和ArrayList在插入数据时性能上的差异:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Vector<Integer> vector = new Vector<Integer>();
        long start = System.currentTimeMillis();
        for(int i=0;i<100000;i++)
            list.add(i);
        long end = System.currentTimeMillis();
        System.out.println("ArrayList进行100000次插入操作耗时:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(int i=0;i<100000;i++)
            vector.add(i);
        end = System.currentTimeMillis();
        System.out.println("Vector进行100000次插入操作耗时:"+(end-start)+"ms");
    }
}

  这段代码在我机器上跑出来的结果是:

  

  进行同样多的插入操作,Vector的耗时是ArrayList的两倍。

  这只是其中的一方面性能问题上的反映。

  另外,由于Vector中的add方法和get方法都进行了同步,因此,在有多个线程进行访问时,如果多个线程都只是进行读取操作,那么每个时刻就只能有一个线程进行读取,其他线程便只能等待,这些线程必须竞争同一把锁。

  因此为了解决同步容器的性能问题,在Java 1.5中提供了并发容器,位于java.util.concurrent目录下,并发容器的相关知识将在下一篇文章中讲述。

2.同步容器真的是安全的吗?

  也有有人认为Vector中的方法都进行了同步处理,那么一定就是线程安全的,事实上这可不一定。看下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test {
    static Vector<Integer> vector = new Vector<Integer>();
    public static void main(String[] args) throws InterruptedException {
        while(true) {
            for(int i=0;i<10;i++)
                vector.add(i);
            Thread thread1 = new Thread(){
                public void run() {
                    for(int i=0;i<vector.size();i++)
                        vector.remove(i);
                };
            };
            Thread thread2 = new Thread(){
                public void run() {
                    for(int i=0;i<vector.size();i++)
                        vector.get(i);
                };
            };
            thread1.start();
            thread2.start();
            while(Thread.activeCount()>10)   {
                 
            }
        }
    }
}

  在我机器上运行的结果:

  

  正如大家所看到的,这段代码报错了:数组下标越界。

  也许有朋友会问:Vector是线程安全的,为什么还会报这个错?很简单,对于Vector,虽然能保证每一个时刻只能有一个线程访问它,但是不排除这种可能:

  当某个线程在某个时刻执行这句时:

1
2
for(int i=0;i<vector.size();i++)
    vector.get(i);

  假若此时vector的size方法返回的是10,i的值为9

  然后另外一个线程执行了这句:

1
2
for(int i=0;i<vector.size();i++)
    vector.remove(i);

  将下标为9的元素删除了。

  那么通过get方法访问下标为9的元素肯定就会出问题了。

  因此为了保证线程安全,必须在方法调用端做额外的同步措施,如下面所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Test {
    static Vector<Integer> vector = new Vector<Integer>();
    public static void main(String[] args) throws InterruptedException {
        while(true) {
            for(int i=0;i<10;i++)
                vector.add(i);
            Thread thread1 = new Thread(){
                public void run() {
                    synchronized (Test.class) {   //进行额外的同步
                        for(int i=0;i<vector.size();i++)
                            vector.remove(i);
                    }
                };
            };
            Thread thread2 = new Thread(){
                public void run() {
                    synchronized (Test.class) {
                        for(int i=0;i<vector.size();i++)
                            vector.get(i);
                    }
                };
            };
            thread1.start();
            thread2.start();
            while(Thread.activeCount()>10)   {
                 
            }
        }
    }
}

 3. ConcurrentModificationException异常

  在对Vector等容器并发地进行迭代修改时,会报ConcurrentModificationException异常,关于这个异常将会在后续文章中讲述。

  但是在并发容器中不会出现这个问题。

  参考资料:

  《深入理解Java虚拟机》

  《Java并发编程实战》

  http://thinkgeek.diandian.com/post/2012-03-24/17905694

  http://blog.csdn.net/cutesource/article/details/5780740

作者:海子
         
本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分享到:
评论

相关推荐

    多线程基础、JUC锁、同步容器、线程池

    多线程基础、JUC锁、同步容器、线程池

    Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器、并发容器、阻塞队列、Synchronizer(比如CountDownLatch)。我们来讨论下同步容器。  一.为什么会出现同步容器?  在...

    Java同步容器和并发容器详解

    主要介绍了Java同步容器和并发容器详解,容器是Java基础类库中使用频率最高的一部分,Java集合包中提供了大量的容器类来帮组我们简化开发,下面小编和大家来一起学习下吧

    Java并发:同步容器&并发容器

    第一节 同步容器、并发容器  1.简述同步容器与并发容器  在Java并发编程中,经常听到同步容器、并发容器之说,那什么是同步容器与并发容器呢?同步容器可以简单地理解为通过synchronized来实现同步的容器,比如...

    Java从同步容器到并发容器的操作过程

    主要介绍了Java从同步容器到并发容器的操作过程,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

    triton-syncthing:Docker和Triton的同步容器

    Triton同步容器使用Consul在服务器上发现另一个Syncthing容器的IP地址。 怎么跑 运行同步设备 泊坞窗运行-d -总是重启-p 8384 -p 22000 -p 21025 / udp --link $ {consul_name}:consul1 xer0x / triton-syncthing...

    并发编程实践,全面介绍基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具、Executor部分等

    详细介绍java并发编程...  同步容器   并发容器   阻塞队列 高级线程协作工具   信号量   闭锁   关卡 fork-join Executor部分   Executor基础 ThreadPoolExecutor的定制   线程的中断与任务的取消   其他

    vue-scroll-sync-同步容器滚动位置的组件-Vue.js开发

    vue-scroll-sync一个同步容器滚动位置的Vue组件安装npm i-保存vue-scroll-sync浏览器包括脚本文件vue-scroll-sync一个同步容器滚动位置的Vue组件安装npm i-保存vue -scroll-sync浏览器包括脚本文件,然后使用Vue.use...

    解决Docker容器时区及时间不同步问题的方法

    主要介绍了解决Docker容器时区及时间不同步问题的方法,有效的解决了Docker容器时间同步问题,有兴趣的可以了解一下。

    Java高并发编程,构建并发编程知识体系,提升面试成功率

    本课程将结合大量图示及代码演示,带你掌握多线程并发编程(线程安全,线程调度,线程封闭,同步容器等)与高并发处理思路与手段(扩容,缓存,队列,拆分等),构建完整的并发与高并发知识体系,胜任实际开发中并发...

    Docker容器间数据同步

    Docker容器间数据同步

    Java高级工程师简历模板18k+

    2.熟练使用常用的java集合类以及常用集合的源码,熟悉多线程以及同步容器以及并发容器的使用,AQS,CAS,lock,volatilte,synchronized等; 3.对Java虚拟机、JMM、垃圾收集机制、GC算法、JVM常用配置参数、GC参数、...

    JAVA并发编程实践_中文版(1-16章全)_1/4

    5.1 同步容器 5.2 发容器 5.3 阻塞队列和生产者一消费者模式 5.4 阻塞和可中断的方法 5.5 synchronizer 5.6 为计算结果建立高效、可伸缩的高速缓存 第2部分 构建并发应用程序 第6章 任务执行 6.1 在线程中执行任务 ...

    Java并发编程实战

    5.1.1 同步容器类的问题 5.1.2 迭代器与Concurrent-ModificationException 5.1.3 隐藏迭代器 5.2 并发容器 5.2.1 ConcurrentHashMap 5.2.2 额外的原子Map操作 5.2.3 CopyOnWriteArrayList 5.3 阻塞队列和...

    Java 并发编程实战

    5.1.1 同步容器类的问题 5.1.2 迭代器与Concurrent-ModificationException 5.1.3 隐藏迭代器 5.2 并发容器 5.2.1 ConcurrentHashMap 5.2.2 额外的原子Map操作 5.2.3 CopyOnWriteArrayList 5.3 阻塞队列和...

    java并发包&线程池原理分析&锁的深度化

    同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经...

    Java并发编程part2

    5.1 同步容器 5.2 发容器 5.3 阻塞队列和生产者一消费者模式 5.4 阻塞和可中断的方法 5.5 synchronizer 5.6 为计算结果建立高效、可伸缩的高速缓存 第2部分 构建并发应用程序 第6章 任务执行 6.1 在线程中执行任务 ...

    Java并发编程实践part1

    5.1 同步容器 5.2 发容器 5.3 阻塞队列和生产者一消费者模式 5.4 阻塞和可中断的方法 5.5 synchronizer 5.6 为计算结果建立高效、可伸缩的高速缓存 第2部分 构建并发应用程序 第6章 任务执行 6.1 在线程中执行任务 ...

    尚硅谷Java视频_JUC 视频教程

    尚硅谷_JUC线程高级_同步容器类ConcurrentHashMap ·5. 尚硅谷_JUC线程高级_CountDownLatch 闭锁 ·6. 实现 Callable 接口 ·7. 尚硅谷_JUC线程高级_同步锁 Lock ·8. 尚硅谷_JUC线程高级_生产者消费者案例-虚假...

    workflow资料

    │ 事件容器 EventDrivenActivity.doc │ │ 代码结点 CodeActivity.doc │ │ 同步容器 SynchronizationScopeActivity.doc │ │ 并行容器 ParallelActivity.doc │ │ 延时结点 DelayActivity.doc...

Global site tag (gtag.js) - Google Analytics