Map 非线程安全的证明 以及线程安全方案(Concurrent)
2016-08-09 20:04
162 查看
参考 http://blog.csdn.net/nx188/article/details/50988037
一、概述
大部分容器(比如:List、Map、Set)都是设计成非线程安全的,因为任何同步都是会损耗性能的。除了部分,如Vector是线程安全的。
线程安全的定义:
比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素1存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B向此 ArrayList 添加元素2,因为此时 Size 仍然等于 0 (注意,我们假设的是添加一个元素是要两个步骤,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值,结果Size等于2。
那好,我们来看看 ArrayList 的情况,期望的元素应该有2个,而实际只有一个元素,造成丢失元素,而且Size 等于 2。这就是“线程不安全”了。
二、代码验证
验证方式通过累加,如果有定义中数据丢失的情况,那么累加出的结果就比正常值小。以map的5种同步方式来验证该业务流程下的线程安全方案。
(1)普通map
(2)synchronized修饰下的map操作集合
(3)ReentrantLock加锁的map操作集合
(4)ReentrantReadWriteLock加锁的map存取操作
(5)collections.synchronizedMap
(6)ConcurrentMap
三、运行截图
四、说明
这里可以看出Map是非线程安全的。但ReentrainReadWriteLock 和 Collections.synchronizedMap 都是为了确保线程安全而构造出来的类,之所以在这里得出的结果不正确是因为:同步的方式与具体的业务逻辑有关。
ReentrainReadWriteLock用在高并发读,低并发写,并且读出的数据与写入的数据无关的情况。例如:云盘上的文件。
Collections.synchronizedMap 的本质是在对Map的每一个操作之前都上一把锁,因此如果读、写操作无关联,则可以使用该方法。比如读写本地记事本。
一、概述
大部分容器(比如:List、Map、Set)都是设计成非线程安全的,因为任何同步都是会损耗性能的。除了部分,如Vector是线程安全的。
线程安全的定义:
比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素1存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B向此 ArrayList 添加元素2,因为此时 Size 仍然等于 0 (注意,我们假设的是添加一个元素是要两个步骤,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值,结果Size等于2。
那好,我们来看看 ArrayList 的情况,期望的元素应该有2个,而实际只有一个元素,造成丢失元素,而且Size 等于 2。这就是“线程不安全”了。
二、代码验证
验证方式通过累加,如果有定义中数据丢失的情况,那么累加出的结果就比正常值小。以map的5种同步方式来验证该业务流程下的线程安全方案。
(1)普通map
(2)synchronized修饰下的map操作集合
(3)ReentrantLock加锁的map操作集合
(4)ReentrantReadWriteLock加锁的map存取操作
(5)collections.synchronizedMap
(6)ConcurrentMap
package com.demo.test; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ThreadSafe { public static final String KEY = "key"; public static final int THREAD_COUNT = 1000; private enum SynType { NONE, SYNCHRONIZED, REENTRANTLOCK, REENTRANTREADWRITELOCK, COLLECTIONS_SYNC } private static Map<String, Long> map = new HashMap<String, Long>(); private static Map<String, Long> collectionsSyncMap = Collections.synchronizedMap(map); private static ConcurrentMap<String, Long> concurrentMap = new ConcurrentHashMap<String, Long>(); private static ReentrantLock reentrantLock = new ReentrantLock(); private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public static long mapIncrease(String key) { Long oldValue = map.get(key); Long newValue = (oldValue == null) ? 1L : oldValue + 1; map.put(key, newValue); return newValue; } public synchronized static long synchronizedMapIncrease(String key) { Long oldValue = map.get(key); Long newValue = (oldValue == null) ? 1L : oldValue + 1; map.put(key, newValue); return newValue; } public static long reentrantLockMapIncrease(String key) { reentrantLock.lock(); Long oldValue = map.get(key); Long newValue = (oldValue == null) ? 1L : oldValue + 1; map.put(key, newValue); reentrantLock.unlock(); return newValue; } public static long readWriteLockMapIncrease(String key) { readWriteLock.readLock().lock(); Long oldValue = map.get(key); Long newValue = (oldValue == null) ? 1L : oldValue + 1; readWriteLock.readLock().unlock(); readWriteLock.writeLock().lock(); map.put(key, newValue); readWriteLock.writeLock().unlock(); return newValue; } public static long collectionsSyncMapIncrease(String key) { Long oldValue = collectionsSyncMap.get(key); Long newValue = (oldValue == null) ? 1L : oldValue + 1; collectionsSyncMap.put(key, newValue); return newValue; } public static long concurrentMapIncrease(String key) { Long oldValue, newValue; while (true) { oldValue = concurrentMap.get(key); if (oldValue == null) { newValue = 1L; if (concurrentMap.putIfAbsent(key, newValue) == null) { break; } } else { newValue = oldValue + 1; if (concurrentMap.replace(key, oldValue, newValue)) { break; } } } return newValue; } public static void calcMapCount(final SynType synType) throws InterruptedException { ArrayList<Thread> list = new ArrayList<Thread>(THREAD_COUNT); for (int i = 0; i < THREAD_COUNT; i++) { Thread thread = new Thread(new Runnable() { public void run() { int count = 0; while (count++ < 10000) { switch (synType) { case NONE: mapIncrease(KEY); break; case SYNCHRONIZED: synchronizedMapIncrease(KEY); break; case REENTRANTLOCK: reentrantLockMapIncrease(KEY); break; case REENTRANTREADWRITELOCK: readWriteLockMapIncrease(KEY); break; case COLLECTIONS_SYNC: collectionsSyncMapIncrease(KEY); break; } } } }); thread.start(); list.add(thread); } for (int i = 0; i < THREAD_COUNT; i++) { list.get(i).join(); } } public static void calcConcurrentMapCount() throws InterruptedException { ArrayList<Thread> list = new ArrayList<Thread>(THREAD_COUNT); for (int i = 0; i < THREAD_COUNT; i++) { Thread thread = new Thread(new Runnable() { public void run() { int count = 0; while (count++ < 10000) ThreadSafe.concurrentMapIncrease(KEY); } }); thread.start(); list.add(thread); } for (int i = 0; i < THREAD_COUNT; i++) { list.get(i).join(); } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; i++) { calcMapCount(SynType.NONE); System.out.println("map = " + map.get(KEY)); map.clear(); calcMapCount(SynType.SYNCHRONIZED); System.out.println("synchronized map = " + map.get(KEY)); map.clear(); calcMapCount(SynType.REENTRANTLOCK); System.out.println("reentrantLockMap = " + map.get(KEY)); map.clear(); calcMapCount(SynType.REENTRANTREADWRITELOCK); System.out.println("reentrantReadWriteLockMap = " + map.get(KEY)); map.clear(); calcMapCount(SynType.COLLECTIONS_SYNC); System.out.println("collections.synchronizedMap = " + collectionsSyncMap.get(KEY)); collectionsSyncMap.clear(); map.clear(); calcConcurrentMapCount(); System.out.println("concurrentMap = " + concurrentMap.get(KEY)); concurrentMap.clear(); } } }
三、运行截图
四、说明
这里可以看出Map是非线程安全的。但ReentrainReadWriteLock 和 Collections.synchronizedMap 都是为了确保线程安全而构造出来的类,之所以在这里得出的结果不正确是因为:同步的方式与具体的业务逻辑有关。
ReentrainReadWriteLock用在高并发读,低并发写,并且读出的数据与写入的数据无关的情况。例如:云盘上的文件。
Collections.synchronizedMap 的本质是在对Map的每一个操作之前都上一把锁,因此如果读、写操作无关联,则可以使用该方法。比如读写本地记事本。
相关文章推荐
- 关于Map线程安全的几种实现方案
- Java集合中那些类是线程安全的 以及 Map线程安全几种实现方法
- HashMap,LinkedHashMap,TreeMap,HashTable,ConcurrentHashMap,ConcurrentSkipListMap 关于k,v是否为null,以及输出排序
- 实现一个无锁的Stack,并写一段测试代码(多线程访问),证明这个Stack是线程安全的。给出程序以及运行的截图。
- 现在有一个城市销售经理,需要从公司出发,去拜访市内的商家,已知他的位置以及商家的位置,但是由于城市道路交通的原因,他只能在左右中选择一个方向,在上下中选择一个方向,现在问他有多少种方案到达商家地址。给定一个地图map及它的长宽n和m,其中1代表经理位置,2代表商家位置,-1代表不能经过的地区,0代表可以经过的地区,请返回方案数,保证一定存在合法路径。保证矩阵的长宽都小于等于10。
- Map 和ConcurrentMap 线程不安全和线程安全证明
- 关于Map线程安全的几种实现方案
- 线程同步模型, 生产者/消费者, 读写同步,线程池,concurrent map.
- Sql Server 2005 与Sql Server Mobile(Sql server 2005 mobile Edition)数据同步步骤以及问题解决方案
- 定时计划任务方案比较以及通过脚本创建计划任务(SchTasks命令)
- 关于ConcurrentLinkedQueue是否线程安全的疑问
- 加密解密时遇到的"不正确的数据"以及"要解密的数据长度无效"问题解决方案
- CAD规划图转换为MapInfo Tab以及ESRI shp格式数据 技术方案
- 水晶报表部署以及相关问题解决方案
- 对比 Route-Map 在 Redistribute 以及 Policy-Routing 中的应用及注意点
- ASP.NET网站发布方案,以及错误解决办法
- Discuz! 6.1~7.0升级后request路径不兼容以及相应修改方案
- 从 page/application/request/session 得到的 map 以及 Map.put(xxx,yyy) 方法
- 对比 Route-Map 在 Redistribute 以及 Policy-Routing 中的应用及注意点
- vbscript以及javascript的事件委托方案