文档章节

线程安全容器类

ksfzhaohui
 ksfzhaohui
发布于 2013/05/02 21:10
字数 867
阅读 135
收藏 1

     线程安全:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

常见的容器类
线程安全类:Vector和Hashtable
线程非安全类:ArrayList和HashMap

HashMap实例:

public class MainClass {
	public static Map<String, String> hashMap = new HashMap<String, String>();

	public static void main(String[] args) throws InterruptedException {
		// hashMap = Collections.synchronizedMap(hashMap);
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 25; i++) {
					hashMap.put(String.valueOf(i), String.valueOf(i));
				}
			}
		};

		Thread t2 = new Thread() {
			public void run() {
				for (int j = 0; j < 25; j++) {
					hashMap.put(String.valueOf(j), String.valueOf(j));
				}
			}
		};

		t1.start();
		t2.start();

		Thread.sleep(1000);

		for (int i = 0; i < 25; i++) {
			System.err.println(i + ":" + hashMap.get(String.valueOf(i)));
		}
	}
}
打印的结果不稳定,经常出现错误的值
0:null
1:1
2:2
3:null
4:4
5:null


原因分析:

public V put(K key, V value) {
...
addEntry(hash, key, value, i);
...
}

void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
    if (size++ >= threshold)
       resize(2 * table.length);
}

void resize(int newCapacity) {
      Entry[] oldTable = table;
      int oldCapacity = oldTable.length;
      if (oldCapacity == MAXIMUM_CAPACITY) {
          threshold = Integer.MAX_VALUE;
          return;
      }

      Entry[] newTable = new Entry[newCapacity];
      transfer(newTable);
      table = newTable;
      threshold = (int)(newCapacity * loadFactor);
}

从代码中,可以看到,如果发现哈希表的大小超过阀值threshold,就会调用resize方法,扩大容量为原来的两倍,而扩大容量的做法是新建一个Entry[]。 
如果在默认情况下,一个HashMap的容量为16,加载因子为0.75,那么阀值就是12,所以在往HashMap中put的值到达12时,它将自动扩容两倍,如果两个线程同时遇到HashMap的大小达到12的倍数时,就很有可能会出现在将oldTable转移到newTable的过程中遇到问题,从而导致最终的HashMap的值存储异常。

JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。HashTable的所有方法都是同步的。
JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为有条件的线程安全类。
JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap,它提供了更高的灵活性。同时进行的读和写操作都可以并发地执行。

ArrayList实例:

public static List<String> list = new ArrayList<String>();

	public static void main(String[] args) throws InterruptedException {
//		list = Collections.synchronizedList(list);
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 25; i++) {
					list.add(String.valueOf(i));
				}
			}
		};

		Thread t2 = new Thread() {
			public void run() {
				for (int j = 25; j < 50; j++) {
					list.add(String.valueOf(j));
				}
			}
		};

		t1.start();
		t2.start();

		Thread.sleep(1000);

		for (int i = 0; i < 50; i++) {
			System.err.println(i + ":" + list.get(i));
		}
	}
结果出现异常:
44:48
45:49
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 46, Size: 46
at java.util.ArrayList.RangeCheck(ArrayList.java:547)
at java.util.ArrayList.get(ArrayList.java:322)

at map.Test.main(Test.java:37)

原因分析:

public boolean add(E e) {
	ensureCapacity(size + 1);  // Increments modCount!!
	elementData[size++] = e;
	return true;
    }

在多线程的操作下导致扩容和赋值操作的不统一导致了出错。
util.concurrent包也提供了一个线程安全的ArrayList替代者CopyOnWriteArrayList。

Vector和Hashtable在各自的方法上都加了synchronized关键字来保持同步。
我们往往把同步的概念仅仅的理解为一种互斥的方式。其实同步不仅可以阻止一个线程看到对象处于不一致的状态中,它还可以保证进入同步方法或同步代码块的每个线程,都看到由同一个锁保护的之前所有的修改效果。

© 著作权归作者所有

共有 人打赏支持
ksfzhaohui

ksfzhaohui

粉丝 320
博文 131
码字总数 166888
作品 3
南京
高级程序员
加载中

评论(1)

wholwh
wholwh
学习了.
并发编程(三):同步容器和并发容器

前言 Java 中有些集合和非线程安全,而有些集合是线程安全,后者又被称为是Java中的同步容器,因为它能满足操作的原子性,保持数据同步。有些容器时Java自带的,而有些是通过Collections提供...

mengdonghui123456
2017/08/15
0
0
java并发问题,java并发容器解决全局变量的并发问题

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

帅的不像男的
2016/04/13
254
0
《Java并发编程实战》读书笔记二:构建线程安全

一、用组合来实现线性安全 1.设计线程安全的类 设计线程安全类的三个基本要素: 1. 找出构成对象状态的所有变量 2. 找出约束状态变量的不变性条件 3. 建立对象状态的并发访问管理策略 要分析...

小七奇奇
08/17
0
0
Java并发编程:同步容器

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

eddie小英俊
2016/06/15
0
0
并发容器之写时拷贝的 List 和 Set

对于一个对象来说,我们为了保证它的并发性,通常会选择使用声明式加锁方式交由我们的 Java 虚拟机来完成自动的加锁和释放锁的操作,例如我们的 synchronized。也会选择使用显式锁机制来主动...

Single_YAM
2017/12/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

xilinx资源

本系列教学视频由赛灵思高级战略应用工程师带领你:从零开始,一步步深入 掌握 HLS 以及 UltraFAST 设计方法,帮助您成为系统设计和算法加速的大拿! http://www.eetrend.com/topics/2018-0...

whoisliang
6分钟前
0
0
=====BJmeter性能测试小接=====

一、性能测试分类 1、负载测试: 通过逐步加压的方法,达到既定的性能阈值的目标,阈值的设定应是小于某个值,如cpu使用率小于等于80% 2、压力测试: 通过逐步加压的方法,使得系统的某些资源...

覃光林
10分钟前
0
0
企业级开源四层负载均衡解决方案--LVS

网盘链接 企业级开源四层负载均衡解决方案--LVS 本课程将在Linux环境下,学习配置使用LVS,对Web集群和MySQL集群进行负载均衡,并结合利用Keepalived实现负载均衡器的高可用,实现对后端Rea...

qq__2304636824
16分钟前
0
0
Windows上安装Spacemacs

emacs安装 下载地址emacs 安装比较简单,解压后执行\bin\addpm.exe即可 emacs配置 emacs的默认配置文件路径和.emacs.d文件夹都是在Windows主目录下的 C:\Users\Administrator\AppData\Roami...

yxmsw2007
31分钟前
0
0
OSChina 周一乱弹 —— 鱼生不值得

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @瘟神灬念:分享新裤子的单曲《没有理想的人不伤心 (Remix版)》: 《没有理想的人不伤心 (Remix版)》- 新裤子 手机党少年们想听歌,请使劲儿戳...

小小编辑
今天
180
9

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部