Java util之常用数据类型特性盘点(Map 四种同步方式的性能比较)

出处:http://www.iteye.com/topic/164644

 

 

  1. 使用 synchronized 关键字,这也是最原始的方法。代码如下 

     

    synchronized(anObject)
    {
    	value = map.get(key);
    }
     

     

     

  2. 使用 JDK1.5 提供的锁(java.util.concurrent.locks.Lock)。代码如下 

     

    lock.lock();
    value = map.get(key);
    lock.unlock();
     

     

     

  3. 实际应用中,可能多数操作都是读操作,写操作较少。针对这种情况,可以使用 JDK1.5 提供的读写锁(java.util.concurrent.locks.ReadWriteLock)。代码如下 

     

    rwlock.readLock().lock();
    value = map.get(key);
    rwlock.readLock().unlock();
     

     

  4. 使用 JDK1.5 提供的 java.util.concurrent.ConcurrentHashMap 类。该类将 Map 的存储空间分为若干块,每块拥有自己的锁,大大减少了多个线程争夺同一个锁的情况。代码如下 

     

    value = map.get(key); //同步机制内置在 get 方法中
     

     

  5. 结论: 

    写了段测试代码,针对这四种方式进行测试,结果见附图。测试内容为 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值