第一节 同步容器、并发容器

1.简述同步容器与并发容器

  在Java并发编程中,经常听到同步容器、并发容器之说,那什么是同步容器与并发容器呢?同步容器可以简单地理解为通过synchronized来实现同步的容器,比如Vector、Hashtable以及SynchronizedList等容器,如果有多个线程调用同步容器的方法,它们将会串行执行。

  可以通过查看Vector、Hashtable等同步容器的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized,但在某些情况下,同步容器不一定就是线程安全的,比如获取最后一个元素或者删除最后一个元素,我们需要实现额外的同步操作:

public static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
} public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}

  虽然上面的方法看起来没有问题,Vector自身的方法也是同步的,但是在多线程环境中还是隐藏着问题。如果有两个线程A,B同时调用上面的两个方法,假设list的大小为10,这里计算得到的lastIndex为9,线程B首先执行了删除操作(多线程之间操作执行的不确定性导致),而后线程A调用了list.get方法,这时就会发生数组越界异常,导致问题的原因就是上面的复合操作不是原子操作,这里可以通过在方法内部使用list对象锁来实现原子操作。

  同步容器会导致多个线程中对容器方法调用的串行执行,降低并发性,因为它们都是以容器自身对象为锁,所以在需要支持并发的环境中,可以考虑使用并发容器来替代。

  并发容器是针对多个线程并发访问而设计的,在jdk5.0引入了concurrent包,其中提供了很多并发容器,如ConcurrentHashMap、CopyOnWriteArrayList等。

  其实同步容器与并发容器都为多线程并发访问提供了合适的线程安全,不过并发容器的可扩展性更高。在Java5之前,程序员们只有同步容器,且在多线程并发访问的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发容器,并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如,在ConcurrentHashMap中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允许任意数量的读线程并发地访问map,并且执行读操作的线程和写操作的线程也可以并发的访问map,同时允许一定数量的写操作线程并发地修改map,所以它可以在并发环境下实现更高的吞吐量,另外,并发容器提供了一些在使用同步容器时需要自己实现的复合操作,包括putIfAbsent等,但是由于并发容器不能通过加锁来独占访问,所以我们无法通过加锁来实现其他复合操作了。

2.参考资料:

(1)http://www.cnblogs.com/dolphin0520/p/3933404.html

第二节 ConcurrentHashMap

1.初识ConcurrentHashMap

针对并发容器中的ConcurrentHashMap,《java并发编程实战》一书有如下这样一段文字:

此处将揭开ConcurrentHashMap的神秘面纱,首先我们看一下ConcurrentHashMap的结构图,如下:

2.详述ConcurrentHashMap

(1)ConcurrentHashMap的并发度

  ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用。

(2)ConcurrentHashMap的锁分离技术

  HashTable容器在竞争激烈的并发环境下效率低下,原因是所有访问HashTable的线程都必须竞争同一把锁。若容器中有多把锁,每一把锁用于锁定容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁并访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

  对比上图(该图摘自网络),同步容器HashTable实现锁的方式是锁整个hash表,而并发容器ConcurrentHashMap的实现方式是锁桶(简单理解就是将整个hash表想象成一大缸水,现在将这大缸里的水分到了几个水桶里,hashTable每次都锁定这个大缸,而ConcurrentHashMap则每次只锁定其中一个 桶)。

ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等常用操作只锁当前需要用到的桶。试想,原来只能一个线程进入,现在却能同时16个线程进入,并发性的提升是显而易见的。

(3)ConcurrentHashMap的remove操作

当对ConcurrentHashMap进行remove操作时,并不是进行简单的节点删除操作

  对比上图,当对ConcurrentHashMap的一个segment(也就是一个桶中的节点)进行remove后,例如,删除节点C,C节点实际并没有被销毁,而是将C节点前面的反转并拷贝到新的链表中,C节点后面的不需要被克隆。这样的操作使并发的读线程不受并发的写线程的干扰,例如,现在有一个读线程读到了A节点,写线程把C删掉了,但是看上图,读线程仍然可以继续读下去;当然,如果在删除C之前读线程读到的是D,那么更不会有影响。

根据上面所提到的在ConcurrentHashMap中删除一个节点并不会立刻被读线程感受到的效果,就是传说中的弱一致性,所以ConcurrentHashMap的迭代器是弱一致性迭代器

3.参考资料:

本小节仅简单概述了ConcurrentHashMap的一些内容,其实现机制等可参考以下优质文章

(1)http://www.cnblogs.com/ITtangtang/p/3948786.html

(2)http://ifeve.com/concurrenthashmap/

(3)http://blog.csdn.net/xuefeng0707/article/details/40834595

java并发:同步容器&并发容器的更多相关文章

  1. java 多线程 同步 观察者 并发集合的一个例子

    //第一版 package com.hra.riskprice; import com.hra.riskprice.SysEnum.Factor_Type; import org.springfram ...

  2. Java多线程同步集合--并发库高级应用

    一.阻塞队列1.在多线程领域,所谓阻塞,在某些情况下会挂起线程,一旦条件满足,被挂起的线程又会自动被唤醒2.ArrayBlockingQueue(效率高)和LinkedBlockingQueue是两个 ...

  3. Java并发——同步容器与并发容器

    同步容器类 早期版本的JDK提供的同步容器类为Vector和Hashtable,JDK1.2 提供了Collections.synchronizedXxx等工程方法,将普通的容器继续包装.对每个共有方 ...

  4. Java并发--同步容器

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

  5. Java并发—同步容器和并发容器

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

  6. Java并发-同步容器篇

    作者:汤圆 个人博客:javalover.cc 前言 官人们好啊,我是汤圆,今天给大家带来的是<Java并发-同步容器篇>,希望有所帮助,谢谢 文章如果有问题,欢迎大家批评指正,在此谢过啦 ...

  7. Java并发编程:并发容器ConcurrentHashMap

    Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...

  8. Java并发编程之并发容器

    解决并发情况下的容器线程安全问题的.给多线程环境准备一个线程安全的容器对象. 线程安全的容器对象: Vector, Hashtable.线程安全容器对象,都是使用 synchronized 方法实现的 ...

  9. JAVA基础(9)——容器(3)——并发容器

    转载:http://blog.csdn.net/weitry/article/details/52964509 JAVA基础系列规划: JAVA基础(1)——基本概念 JAVA基础(2)——数据类型 ...

  10. 多线程六 同步容器&并发容器

    同步容器(使用的是synchronized,并且不一定是百分百安全) 本篇续 -- 线程之间的通信 ,介绍java提供的并发集合,既然正确的使用wait和notify比较困难,java平台为我们提供了 ...

随机推荐

  1. cacti监控mysql

    cacti监控mysql 2013-09-25 16:21:43 分类: LINUX 原文地址:cacti监控mysql 作者:baochenggood cacti监控mysql 1 下载cacti监 ...

  2. Python基础s14-day1

    2016年7月23日"Python基础s14-Day1" Python是什么? Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/),是一种面向对象.直译式 ...

  3. label与input间距的小问题

    先码后文 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...

  4. Android初涉及之Android Studio&JAVA入门--二月不能不写东西

    是的,我还没有放弃写博客. 是的,我也没有放弃PHP的学习. 是的,我要开始学学最TM火的Android开发了. 你呢 1.Android Studio 一.概况 安装和配置什么的就不具体说了,网上一 ...

  5. Python教程[廖雪峰],主要是实践

  6. session和cookie的区别和联系

    使用session会在客户端生成一个文件,这个文件是以session_id来命名,用来保存文件:生成的文件保存在这个路径中:session.save_path = "D:/wampstack ...

  7. java的string常用操作

    import java.util.*; public class Demo3 { public static void main(String args[]){ String str = " ...

  8. java类型转换

    //java类型转换public class Demo2 { public static void main(String[] args){ int num1 = 55; int num2 =77; ...

  9. MongoDB集群架构及搭建

    MongoDB分布式集群 MongDB分布式集群能够对数据进行备份,提高数据安全性,以及提高集群提高读写服务的能力和数据存储能力.主要通过副本集(replica)对数据进行备份,通过分片(shardi ...

  10. workqueue机制分析之wb_workfn函数

    上面一篇文章说到: process_one_work中最重要的一件事情就是worker->current_func(work); 这里就具体到一项很具体的任务了,由于我要研究文件系统嘛,所以很自 ...