文档章节

线程安全容器类

ksfzhaohui
 ksfzhaohui
发布于 2013/05/02 21:10
字数 867
阅读 138
收藏 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

粉丝 342
博文 137
码字总数 183085
作品 3
南京
高级程序员
私信 提问
加载中

评论(1)

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

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

mengdonghui123456
2017/08/15
0
0
并发实战 之「 基础构建模块」

版权声明:Follow your heart and intuition. https://blog.csdn.net/qq_35246620/article/details/84171738 委托是创建线程安全类的一个最有效的策略:只需让现有的线程安全类管理所有的状态...

维C果糖
11/17
0
0
《Java并发编程实战》读书笔记二:构建线程安全

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

小七奇奇
08/17
0
0
java并发问题,java并发容器解决全局变量的并发问题

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

帅的不像男的
2016/04/13
254
0
Java并发编程:同步容器

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

eddie小英俊
2016/06/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周一乱弹 —— 温柔的人应该这样

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @clouddyy :#每日一歌# 《フィクション-sumika》 《フィクション-sumika》 手机党少年们想听歌,请使劲儿戳(这里) 假期时间干嘛去, @for...

小小编辑
41分钟前
10
4
[LintCode] Serialize and Deserialize Binary Tree(二叉树的序列化和反序列化)

描述 设计一个算法,并编写代码来序列化和反序列化二叉树。将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。 如何反序列化或序列化二叉树是没有限制的,你...

honeymose
今天
6
0
java框架学习日志-7(静态代理和JDK代理)

静态代理 我们平时去餐厅吃饭,不是直接告诉厨师做什么菜的,而是先告诉服务员点什么菜,然后由服务员传到给厨师,相当于服务员是厨师的代理,我们通过代理让厨师炒菜,这就是代理模式。代理...

白话
今天
27
0
Flink Window

1.Flink窗口 Window Assigner分配器。 窗口可以是时间驱动的(Time Window,例如:每30秒钟),也可以是数据驱动的(Count Window,例如:每一百个元素)。 一种经典的窗口分类可以分成: 翻...

满小茂
今天
19
0
my.ini

1

architect刘源源
今天
16
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部