出处:http://www.iteye.com/topic/164644
-
使用 synchronized 关键字,这也是最原始的方法。代码如下
synchronized(anObject) { value = map.get(key); }
-
使用 JDK1.5 提供的锁(java.util.concurrent.locks.Lock)。代码如下
lock.lock(); value = map.get(key); lock.unlock();
-
实际应用中,可能多数操作都是读操作,写操作较少。针对这种情况,可以使用 JDK1.5 提供的读写锁(java.util.concurrent.locks.ReadWriteLock)。代码如下
rwlock.readLock().lock(); value = map.get(key); rwlock.readLock().unlock();
-
使用 JDK1.5 提供的 java.util.concurrent.ConcurrentHashMap 类。该类将 Map 的存储空间分为若干块,每块拥有自己的锁,大大减少了多个线程争夺同一个锁的情况。代码如下
value = map.get(key); //同步机制内置在 get 方法中
-
结论:
写了段测试代码,针对这四种方式进行测试,结果见附图。测试内容为 1 秒钟所有 get 方法调用次数的总和。为了比较,增加了未使用任何同步机制的情况(非安全!)。理论上,不同步应该最快。
我的 CPU 是双核的(Core 2 Duo E6300),因此太多线程也没啥意义,所以只列出了单线程、两个线程和五个线程的情况。更多线程时,CPU 利用率提高,但增加了线程调度的开销,测试结果与五个线程差不多。
从附图可以看出:
1、不同步确实最快,与预期一致。
2、四种同步方式中,ConcurrentHashMap 是最快的,接近不同步的情况。
3、synchronized 关键字非常慢,比使用锁慢了两个数量级。真是大跌眼镜,我很迷惑为什会 synchronized 慢到这个程度。
4、使用读写锁的读锁,比普通所稍慢。这个比较意外,可能硬件或测试代码没有发挥出读锁的全部功效。因此总结如下:
1、如果 ConcurrentHashMap 够用,则使用 ConcurrentHashMap。
2、如果需自己实现同步,则使用 JDK1.5 提供的锁机制,避免使用 synchronized 关键字。上图为原作者的数据,下图是我的机器实测数据:
CPU:Core 2 Duo T6570 双核 内存2G
之所以增加了一个16线程,是因为ConcurrentHashMap使用了16个分离锁。。好象是。。。
一个线程
两个线程
五个线程
16个线程
方法1(使用synchronized关键字
7,542,649
3,920,280
3,731,002
4,178,659
方法2(使用Lock)
7,737,160
4,532,817
5,392,027
5,591,125
方法3(使用ReadLock)
5,935,673
6,109,870
5,996,307
8633877(6013883)
方法4(使用ConcurrentHashMap)
10,108,220
19,989,279
23,787,126
23,384,236
方法0(不使用任何同步机制,非安全)
13,393,506
24,813,267
28,846,768
31,234,310
(注:上图红色部分为最小值,读写分离锁波动比较大,所以记录了最小值)
(注:内部锁在jdk6上已经有比较大的改进,而原作者使用的是JDK1.5版本因此上表的数据可能和原作者的数据有少许偏差,有兴趣可以测试不同JDK版本之间的差异)
测试代码
import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.*; public class MapTest { public static final int THREAD_COUNT = 1; public static final int MAP_SIZE = 1000; public static final int EXECUTION_MILLES = 1000; public static final int[] KEYS = new int[100]; public static void main(String[] args) throws Exception { //初始化 Random rand = new Random(); for (int i = 0; i < KEYS.length; ++i) { KEYS[i] = rand.nextInt(); } //创建线程 long start = System.currentTimeMillis(); Thread[] threads = new Thread[THREAD_COUNT]; for (int i = 0; i < THREAD_COUNT; ++i) { //threads[i] = new UnsafeThread(); //threads[i] = new SynchronizedThread(); //threads[i] = new LockThread(); //threads[i] = new ReadLockThread(); threads[i] = new ConcurrentThread(); threads[i].start(); } //等待其它线程执行若干时间 Thread.sleep(EXECUTION_MILLES); //统计 get 操作的次数 long sum = 0; for (int i = 0; i < THREAD_COUNT; ++i) { sum += threads[i].getClass().getDeclaredField("count").getLong(threads[i]); } long millisCost = System.currentTimeMillis() - start; System.out.println(sum + "(" + (millisCost) + "ms)"); System.exit(0); } public static void fillMap(Map<Integer, Integer> map) { Random rand = new Random(); for (int i = 0; i < MAP_SIZE; ++i) { map.put(rand.nextInt(), rand.nextInt()); } } } class UnsafeThread extends Thread { private static Map<Integer, Integer> map = new HashMap<Integer, Integer>(); public long count = 0; static { MapTest.fillMap(map); } public void run() { for (;;) { int index = (int)(count % MapTest.KEYS.length); map.get(MapTest.KEYS[index]); ++count; } } } class SynchronizedThread extends Thread { private static Map<Integer, Integer> map = new HashMap<Integer, Integer>(); public long count = 0; static { MapTest.fillMap(map); } public void run() { for (;;) { int index = (int)(count % MapTest.KEYS.length); synchronized(SynchronizedThread.class) { map.get(MapTest.KEYS[index]); } ++count; } } } class LockThread extends Thread { private static Map<Integer, Integer> map = new HashMap<Integer, Integer>(); private static Lock lock = new ReentrantLock(); public long count = 0; static { MapTest.fillMap(map); } public void run() { for (;;) { int index = (int)(count % MapTest.KEYS.length); lock.lock(); map.get(MapTest.KEYS[index]); lock.unlock(); ++count; } } } class ReadLockThread extends Thread { private static Map<Integer, Integer> map = new HashMap<Integer, Integer>(); private static Lock lock = new ReentrantReadWriteLock().readLock(); public long count = 0; static { MapTest.fillMap(map); } public void run() { for (;;) { int index = (int)(count % MapTest.KEYS.length); lock.lock(); map.get(MapTest.KEYS[index]); lock.unlock(); ++count; } } } class ConcurrentThread extends Thread { private static Map<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>(); public long count = 0; static { MapTest.fillMap(map); } public void run() { for (;;) { int index = (int)(count % MapTest.KEYS.length); map.get(MapTest.KEYS[index]); ++count; } } }
2011-02-24 david.wang
发表评论
-
JVM内存模型及垃圾收集策略解析
2011-09-25 01:56 858一 JVM内存模型 1.1 Java栈 Java栈 ... -
JVM基础概念总结:数据类型、堆与栈
2011-09-25 01:14 844数据类型 Java虚拟机中,数据类型可以分为两类:基本类型 ... -
JVM分代垃圾回收策略的基础概念
2011-09-25 01:07 718为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不 ... -
理解静态变量惰性初始化的双检锁模式
2011-05-10 22:28 1215对于双检锁,其实有多种不同的用法,有很多种用法是无论 ... -
基本类型 加减运算 类型转换问题讨论
2011-03-09 14:56 969先来看下面的 代码: public class Tes ... -
DataSource 使用方法
2011-03-03 14:26 3015转自:http://marshal.easymorse.com ... -
Java util之常用数据类型特性盘点 (HasMap深度分析)
2011-02-24 17:57 1263出处:http://www.iteye.com/topic/7 ... -
Java util之常用数据类型特性盘点 (三)
2011-02-24 16:22 1088Java util之常用数据类型特性盘点 (三) 出处 ... -
Java util之常用数据类型特性盘点 (二)
2011-02-24 16:07 1062Java util之常用数据类型特性盘点 (二) 出处 ... -
Java util之常用数据类型特性盘点(一)
2011-02-24 15:47 996Java util之常用数据类型特性盘点 出处:http:// ... -
Java 中实例化对象方法
2011-02-24 13:57 10571、直接 new 调用默认构造器实例化对象,如: ... -
java 冒泡排序、选择排序、插入排序、快速排序二分法查找代码
2011-02-23 20:42 4069java 排序的代码 import java.util ... -
java collection集合框架
2011-02-23 17:19 1383Java集合框架使用总 ... -
java.sql.Date与java.util.Date 异同
2011-02-23 17:13 1329java.util.Date 包含日期和时间。 ... -
类的初始化顺序问题
2011-02-23 16:10 734一道面试题目,看代码: public class ... -
java clone方法使用详解
2011-02-23 15:39 905java clone方法使用 出处 ... -
Java数据类型
2011-02-23 15:04 705Java数据类型 JAVA语言中定义了8种基本的数据类型, ... -
java float double为什么精度会丢失
2011-02-23 14:39 5750由于对float或double 的使用不当,可能会出现精度丢失 ... -
java基础知识回顾(1)float/double 与封装类型Float/Double
2011-02-23 14:31 3541float/double 与封装类型Float/Double ... -
HashSet \LinkedHashSet\TreeSet 元素顺序
2011-02-23 13:36 1284import java.util.Arrays; im ...
相关推荐
在Java中,java.util.stream.Collectors.toMap()方法是一个非常实用的工具,它允许我们将流(Stream)中的元素收集到一个Map中。这个方法是Collectors类中的一个静态方法,它实现了Collector接口,用于在流的终止...
14. **Stream**: Java 8的新特性,提供了一种新的数据处理方式,可以对集合进行函数式编程。 在你提供的压缩包`ZycUtil`中,可能包含了上述或其他自定义的Util类,这些类可能是对Java标准库Util类的扩展或补充,...
java util工具类2java util工具类2java util工具类2java util工具类2
在Java编程中,工具类(Util Class)是包含各种实用函数的静态类,它们提供了一种简化常见任务的方法。在给定的`UtilClass`中,我们有五个主要的工具类:`StringUtil`、`FileUtil`、`ConnectDB`、`DateUtil`和`...
Java中的`util`工具类是Java Standard Edition (Java SE)库中的重要组成部分,它们提供了许多实用功能,极大地简化了开发工作。在这个主题中,我们将深入探讨`StringUtil`、`FileUtil`、`MD5`、`JsonUtil`以及`...
在Java编程语言中,`util`包是Java标准库中的一个核心部分,包含了大量实用工具类,极大地丰富了开发者的代码库。这个包下有很多重要的类,如ArrayList、HashMap、LinkedList、Date、Calendar等,它们提供了许多常用...
13. **Stream API**:自Java 8起,`java.util.stream`包提供了Stream API,它允许以声明性方式处理数据,适用于大量数据的处理和并行计算。 14. **EnumSet和EnumMap**:专门为枚举类型设计的集合,提供了高效的存储...
在Java中,Jython是一个非常有用的工具,它允许我们在Java应用程序中嵌入Python代码,并能够无缝地在Python数据类型和Java数据类型之间进行转换。这极大地扩展了Java的生态系统,让我们能够利用Python丰富的库和简洁...
在Java编程中,Pojo(Plain Old Java Object)是一种简单的对象模型,通常用于表示数据实体。Map接口则是Java集合框架的一部分,它提供了键值对的数据存储方式,方便数据的存取。将Pojo对象转换为Map,可以简化数据...
Java.util包是Java标准库中的核心包之一,它包含了大量用于通用编程的类和接口,是Java开发中不可或缺的一部分。这个包提供了数据结构、集合框架、事件处理、日期时间、随机数生成、位集以及与I/O流操作相关的辅助...
Mysql、Oracle 中的数据类型与 Java 中的数据类型对应表 MySQL 和 Oracle 是两种常用的关系型数据库管理系统,而 Java 是一种广泛应用于企业级应用开发的编程语言。在数据库应用开发中,理解数据类型的对应关系...
这是我在编写struts2中遇到的问题,整理出来,包括截图,希望可以帮到大家
Java的`util`工具类是Java Standard Library中的核心部分,包含了大量的实用类和接口,极大地提高了开发效率。这里我们将深入探讨几个在标题和描述中提到的`util`工具类及其在实际项目中的应用。 1. **MD5加密**: `...
Java Util是Java编程语言中的一个核心包,包含了各种实用工具类,这些类提供了大量的静态方法,用于处理数组、集合、日期时间、字符流等常见任务。这个包的重要性在于它为开发者提供了一组基础且强大的工具,极大地...
本文将深入探讨Java中的一些关键转换技术,包括基本数据类型的转换、JSONObject到Map的转换以及JSONArray到List的转换,并提供相关的源码示例。 一、基本数据类型转换 1. 自动类型转换:当我们将一个范围小的数据...
Java工具类(Java Util)是Java开发中不可或缺的一部分,它为开发者提供了大量便捷的功能,极大地提高了开发效率。在Java标准库中,`java.util`包是核心工具类库,包含了各种容器类、集合框架、日期时间处理、随机数...
### 使用 Java.util.zip 包实现数据压缩与解压 在计算机科学领域,数据压缩技术是一项重要的功能,它能够帮助减少存储空间的需求以及提高网络传输效率。本文将通过一系列的示例来详细介绍如何利用 Java 中的 `java....
在Java中,`java.util` 包是最基础也是最常用的工具类集合,而"Java常用Util类Jar"则是一个包含了更多自定义或者网络上流行实用工具类的集合,适用于Web项目的快速开发。 这个压缩包中的`ZycUtil`可能是一个自定义...
在Java开发中,特别是在使用Spring框架时,将数据库中的数据转换为Map对象是一种常见的操作。这样做可以简化数据处理,使得数据以键值对的形式存储,便于快速查找和使用。Spring框架提供了强大的数据访问抽象,包括...
同步器是一种抽象数据类型(Abstract Data Type, ADT),它维护了一个内部同步状态(例如表示一个锁是否被锁定),提供了更新和检查该状态的方法,以及至少一个方法,当满足某些条件时会使调用线程阻塞,直到其他...