HashMap
是Java中非常强大的数据结构。我们每天都在几乎所有应用程序中使用它。我之前写过很多关于如何实现Threadsafe缓存,如何将Hashmap转换为Arraylist的示例。
我们在以上两个示例中都使用了Hashmap,但是这些是Hashmap的非常简单的用例。HashMap is a non-synchronized
集合类。
您有以下任何问题吗?
- ConcurrentHashMap和Collections.synchronizedMap(Map)有什么区别?
- 在性能方面,ConcurrentHashMap和Collections.synchronizedMap(Map)有什么区别?
- ConcurrentHashMap与Collections.synchronizedMap()
- 热门的HashMap和ConcurrentHashMap面试问题
在本教程中,我们将讨论以上所有查询以及why and how
我们可以同步Hashmap的原因 吗?
为什么?
Map对象是一个存储元素的关联容器,这些元素是由唯一标识key
和mapd的组合形成的value
。如果您有非常高的并发应用程序,您可能要在其中修改或读取不同线程中的键值,那么最好使用并发哈希图。最好的例子是Producer Consumer ,它处理并发读/写。
那么线程安全Map的含义是什么?如果同时multiple threads
访问一个哈希映射,并且至少有一个线程在结构上修改了该映射,则must be synchronized externally
可以避免内容的不一致视图。
如何?
有两种方法可以同步HashMap
- Java CollectionssyncedMap()方法
- 使用ConcurrentHashMap
//Hashtable
Map<String, String> normalMap = new Hashtable<String, String>();
//synchronizedMap
synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
//ConcurrentHashMap
concurrentHashMap = new ConcurrentHashMap<String, String>();
并发哈希图
- 当您的项目中需要很高的并发时,应该使用ConcurrentHashMap。
- 它是线程安全的,无需同步
whole map
。 - 用锁完成写操作时,读操作可能会非常快。
- 在对象级别没有锁定。
- 在哈希图存储桶级别,锁定的粒度要精细得多。
ConcurrentModificationException
如果一个线程试图修改它,而另一个线程对其进行迭代,则ConcurrentHashMap不会抛出。- ConcurrentHashMap使用多个锁。
SynchronizedHashMap
- 对象级别的同步。
- 每个读/写操作都需要获取锁定。
- 锁定整个集合是性能开销。
- 本质上,这仅允许访问整个地图的一个线程,并阻止所有其他线程。
- 它可能会引起争用。
- SynchronizedHashMap返回
Iterator
,在并发修改时快速失败。
现在让我们看一下代码
- 建立课程
CrunchifyConcurrentHashMapVsSynchronizedHashMap.java
- 为每个HashTable,SynchronizedMap和CrunchifyConcurrentHashMap创建对象
- 从地图添加和检索500k条目
- 测量开始和结束时间以及显示时间(以毫秒为单位)
- 我们将使用ExecutorService
5 threads
并行运行
这是一个Java代码:
package CrunchifyConcurrentHashMapVsSynchronizedMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @author Crunchify.com
*
*/
public class CrunchifyConcurrentHashMapVsSynchronizedMap {
public final static int THREAD_POOL_SIZE = 5;
public static Map<String, Integer> crunchifyHashTableObject = null;
public static Map<String, Integer> crunchifySynchronizedMapObject = null;
public static Map<String, Integer> crunchifyConcurrentHashMapObject = null;
public static void main(String[] args) throws InterruptedException {
// Test with Hashtable Object
crunchifyHashTableObject = new Hashtable<String, Integer>();
crunchifyPerformTest(crunchifyHashTableObject);
// Test with synchronizedMap Object
crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>());
crunchifyPerformTest(crunchifySynchronizedMapObject);
// Test with ConcurrentHashMap Object
crunchifyConcurrentHashMapObject = new ConcurrentHashMap<String, Integer>();
crunchifyPerformTest(crunchifyConcurrentHashMapObject);
}
public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException {
System.out.println("Test started for: " + crunchifyThreads.getClass());
long averageTime = 0;
for (int i = 0; i < 5; i++) {
long startTime = System.nanoTime();
ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
for (int j = 0; j < THREAD_POOL_SIZE; j++) {
crunchifyExServer.execute(new Runnable() {
@SuppressWarnings("unused")
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000);
// Retrieve value. We are not using it anywhere
Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));
// Put value
crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);
}
}
});
}
// Initiates an orderly shutdown in which previously submitted tasks are
// executed, but no new tasks will be accepted. Invocation
// has no additional effect if already shut down.
// This method does not wait for previously submitted tasks to complete
// execution. Use awaitTermination to do that.
crunchifyExServer.shutdown();
// Blocks until all tasks have completed execution after a shutdown request, or
// the timeout occurs, or the current thread is
// interrupted, whichever happens first.
crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
long entTime = System.nanoTime();
long totalTime = (entTime - startTime) / 1000000L;
averageTime += totalTime;
System.out.println("500K entried added/retrieved in " + totalTime + " ms");
}
System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n");
}
}
shutdown()
意味着执行程序服务不再执行传入的任务。awaitTermination()
在关闭请求后调用。
因此,您需要首先关闭serviceExecutor,然后阻止并等待线程完成。
Eclipse控制台结果:
Test started for: class java.util.Hashtable
500K entried added/retrieved in 2713 ms
500K entried added/retrieved in 2321 ms
500K entried added/retrieved in 2257 ms
500K entried added/retrieved in 2126 ms
500K entried added/retrieved in 2650 ms
For class java.util.Hashtable the average time is 2413 ms
Test started for: class java.util.Collections$SynchronizedMap
500K entried added/retrieved in 2366 ms
500K entried added/retrieved in 2805 ms
500K entried added/retrieved in 2694 ms
500K entried added/retrieved in 2760 ms
500K entried added/retrieved in 2459 ms
For class java.util.Collections$SynchronizedMap the average time is 2616 ms
Test started for: class java.util.concurrent.ConcurrentHashMap
500K entried added/retrieved in 1285 ms
500K entried added/retrieved in 760 ms
500K entried added/retrieved in 726 ms
500K entried added/retrieved in 746 ms
500K entried added/retrieved in 707 ms
For class java.util.concurrent.ConcurrentHashMap the average time is 844 ms