文档章节

线程安全容器类

ksfzhaohui
 ksfzhaohui
发布于 2013/05/02 21:10
字数 867
阅读 134
收藏 1
点赞 0
评论 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

粉丝 296
博文 126
码字总数 153828
作品 3
南京
高级程序员
加载中

评论(1)

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

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

mengdonghui123456 ⋅ 2017/08/15 ⋅ 0

java并发问题,java并发容器解决全局变量的并发问题

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

帅的不像男的 ⋅ 2016/04/13 ⋅ 0

Java并发编程:同步容器

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

eddie小英俊 ⋅ 2016/06/15 ⋅ 0

并发容器之写时拷贝的 List 和 Set

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

Single_YAM ⋅ 2017/12/02 ⋅ 0

对称锁死锁

问题分析见文章链接:对称锁死锁,对应的英文文章:Synchronization on mutable fields 具体内容: 可以考虑使用一个线程安全容器类 — 一个保证用户操作线程安全的数据结构。(这与 中的大多...

李矮矮 ⋅ 2016/10/17 ⋅ 0

探秘Java并发模块:容器与工具类

并发与多线程是每个人程序员都头疼的内容,幸好Java库所提供了丰富并发基础模块,这些多线程安全的模块作为并发工具类将帮助大家来应对并发开发的各种需求。 扩展阅读: 多线程安全性:每个人...

登高且赋 ⋅ 2017/11/11 ⋅ 0

Java servlet多线程

Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet 时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户...

hanzhankang ⋅ 2014/02/09 ⋅ 0

【转】 Servlet多线程安全问题

一,servlet容器如何同时处理多个请求 Servlet采用多线程来处理多个请求同时访问,Servelet容器维护了一个线程池来服务请求。该线程池实际上是等待执行代码的一组线程,我们把它叫做工作者线...

mj4738 ⋅ 2012/05/24 ⋅ 0

Servlet 单例多线程

深入研究Servlet线程安全性问题 http://www.cnblogs.com/gw811/archive/2012/09/07/2674859.html Servlet 单例多线程 Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方...

城固如春 ⋅ 2016/10/23 ⋅ 0

Java学习资料-Servlet单例多线程

Servlet 单例多线程 Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并...

晓阳 ⋅ 2015/02/26 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

来自一个优秀Java工程师的简历

写在前面: 鉴于前几天的一份前端简历,虽然带着很多不看好的声音,但却帮助了很多正在求职路上的人,不管评论怎么说,我还是决定要贴出一份后端的简历。 XXX ID:357912485 目前正在找工作 ...

颖伙虫 ⋅ 23分钟前 ⋅ 0

Confluence 6 恢复一个站点有关使用站点导出为备份的说明

推荐使用生产备份策略。我们推荐你针对你的生产环境中使用的 Confluence 参考 Production Backup Strategy 页面中的内容进行备份和恢复(这个需要你备份你的数据库和 home 目录)。XML 导出备...

honeymose ⋅ 今天 ⋅ 0

JavaScript零基础入门——(九)JavaScript的函数

JavaScript零基础入门——(九)JavaScript的函数 欢迎回到我们的JavaScript零基础入门,上一节课我们了解了有关JS中数组的相关知识点,不知道大家有没有自己去敲一敲,消化一下?这一节课,...

JandenMa ⋅ 今天 ⋅ 0

火狐浏览器各版本下载及插件httprequest

各版本下载地址:http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/ httprequest插件截至57版本可用

xiaoge2016 ⋅ 今天 ⋅ 0

Docker系列教程28-实战:使用Docker Compose运行ELK

原文:http://www.itmuch.com/docker/28-docker-compose-in-action-elk/,转载请说明出处。 ElasticSearch【存储】 Logtash【日志聚合器】 Kibana【界面】 答案: version: '2'services: ...

周立_ITMuch ⋅ 今天 ⋅ 0

使用快嘉sdkg极速搭建接口模拟系统

在具体项目研发过程中,一旦前后端双方约定好接口,前端和app同事就会希望后台同事可以尽快提供可供对接的接口方便调试,而对后台同事来说定好接口还仅是个开始、设计流程,实现业务逻辑,编...

fastjrun ⋅ 今天 ⋅ 0

PXE/KickStart 无人值守安装

导言 作为中小公司的运维,经常会遇到一些机械式的重复工作,例如:有时公司同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装。 常规的办法有什么? 光盘安装系统 ===> 一...

kangvcar ⋅ 昨天 ⋅ 0

使用Puppeteer撸一个爬虫

Puppeteer是什么 puppeteer是谷歌chrome团队官方开发的一个无界面(Headless)chrome工具。Chrome Headless将成为web应用自动化测试的行业标杆。所以我们很有必要来了解一下它。所谓的无头浏...

小草先森 ⋅ 昨天 ⋅ 0

Java Done Right

* 表示难度较大或理论性较强。 ** 表示难度更大或理论性更强。 【Java语言本身】 基础语法,面向对象,顺序编程,并发编程,网络编程,泛型,注解,lambda(Java8),module(Java9),var(...

风华神使 ⋅ 昨天 ⋅ 0

Linux系统日志

linux 系统日志 /var/log/messages /etc/logrotate.conf 日志切割配置文件 https://my.oschina.net/u/2000675/blog/908189 logrotate 使用详解 dmesg 命令 /var/log/dmesg 日志 last命令,调......

Linux学习笔记 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部