HashMap与 ConcurrentHashMap与。SynchronizedMap –如何在Java中同步HashMap

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

  1. Java CollectionssyncedMap()方法
  2. 使用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,在并发修改时快速失败。

现在让我们看一下代码

  1. 建立课程 CrunchifyConcurrentHashMapVsSynchronizedHashMap.java
  2. 为每个HashTable,SynchronizedMap和CrunchifyConcurrentHashMap创建对象
  3. 从地图添加和检索500k条目
  4. 测量开始和结束时间以及显示时间(以毫秒为单位)
  5. 我们将使用ExecutorService5 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

 

 

猜你喜欢

转载自blog.csdn.net/allway2/article/details/115352385