如何同步Java HashMap

时间:2020-01-09 10:35:04  来源:igfitidea点击:

这篇文章介绍了如何使用Java同步HashMap和HashMap的线程安全替代方法(可以使用)。

HashMap不是线程安全的

如果多个线程同时访问HashMap,并且至少有一个线程在结构上修改了映射,则必须在外部同步HashMap。请注意,结构修改是添加或者删除一个或者多个映射的任何操作。仅更改与实例已经包含的键相关联的值并不是结构上的修改。

线程安全映射的选项

如果要在Java中同步HashMap或者寻找HashMap的线程安全替代方案,则有以下选项。

  • 使用Collections.synchronizedMap()同步Map此方法返回指定映射支持的同步(线程安全)映射。如果要同步TreeMap(这是一个排序的Map),则可以使用syncedSortedMap()方法
  • 使用ConcurrentHashMap的另一个选择是使用java.util.concurrent包中的ConcurrentHashMap。它的所有操作都是线程安全的,并且提供了更好的并发性。 ConcurrentHashMap通过为单独的存储桶使用单独的锁而不是在单个锁上同步整个Map来提供更好的性能。

使用Collections.synchronizedMap()

我们可以使用Collections.synchronizedMap()方法来同步HashMap。首先,我们将看到一个示例,如果在多线程环境中使用HashMap而不同步它会发生什么。

在Java示例中,创建了四个线程,每个线程向Map添加5个元素。完成所有线程后,映射大小应为20。

import java.util.HashMap;
import java.util.Map;

public class MapSynchro implements Runnable{
  private Map<String, String> testMap;
  public MapSynchro(Map<String, String> testMap){
    this.testMap = testMap;
  }

  public static void main(String[] args) {
    // Synchronized Map
    Map<String, String> testMap = new HashMap<String, String>();
    /// 4 threads
    Thread t1 = new Thread(new MapSynchro(testMap));
    Thread t2 = new Thread(new MapSynchro(testMap));
    Thread t3 = new Thread(new MapSynchro(testMap));
    Thread t4 = new Thread(new MapSynchro(testMap));
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();
        
    try {
      t1.join();
      t2.join();
      t3.join();
      t4.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
    System.out.println("Size of Map is " + testMap.size());
  }
    
  @Override
  public void run() {
    System.out.println("in run method" + Thread.currentThread().getName());
    String str = Thread.currentThread().getName();
    for(int i = 0; i < 5; i++){
      // adding thread name to make element unique
      testMap.put(str+i, str+i);
      try {
        // delay to verify thread interference
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

输出:

in run methodThread-1
in run methodThread-0
in run methodThread-2
in run methodThread-3
Size of Map is 19

在不同的运行中,由于线程干扰,我得到了不同的HashMap大小,分别为17、18、19和20。

现在,如果我们使用Collections.synchronizedMap()方法在同一Java示例中同步HashMap。

public class MapSynchro implements Runnable{
  private Map<String, String> testMap;
  public MapSynchro(Map<String, String> testMap){
    this.testMap = testMap;
  }

  public static void main(String[] args) {
    // Synchronized Map
    Map<String, String> testMap = Collections.synchronizedMap(new HashMap<String, String>());
    /// 4 threads
    Thread t1 = new Thread(new MapSynchro(testMap));
    Thread t2 = new Thread(new MapSynchro(testMap));
    Thread t3 = new Thread(new MapSynchro(testMap));
    Thread t4 = new Thread(new MapSynchro(testMap));
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();
        
    try {
      t1.join();
      t2.join();
      t3.join();
      t4.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
    System.out.println("Size of Map is " + testMap.size());
  }
  @Override
  public void run() {
    System.out.println("in run method" + Thread.currentThread().getName());
    String str = Thread.currentThread().getName();
    for(int i = 0; i < 5; i++){
      // adding thread name to make element unique
      testMap.put(str+i, str+i);
      try {
        // delay to verify thread interference
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

输出:

in run methodThread-2
in run methodThread-0
in run methodThread-3
in run methodThread-1
Size of Map is 20

现在,地图的大小每次都是20。

使用ConcurrentHashMap

除了同步HashMap之外,拥有线程安全的HashMap的另一种方法是在Java中使用ConcurrentHashMap。让我们看看使用ConcurrentHashMap的上述示例。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MapSynchro implements Runnable{
  private Map<String, String> testMap;
  public MapSynchro(Map<String, String> testMap){
    this.testMap = testMap;
  }

  public static void main(String[] args) {
    // Synchronized Map
    Map<String, String> testMap = new ConcurrentHashMap<String, String>();
    /// 4 threads
    Thread t1 = new Thread(new MapSynchro(testMap));
    Thread t2 = new Thread(new MapSynchro(testMap));
    Thread t3 = new Thread(new MapSynchro(testMap));
    Thread t4 = new Thread(new MapSynchro(testMap));
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();
        
    try {
      t1.join();
      t2.join();
      t3.join();
      t4.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
    System.out.println("Size of Map is " + testMap.size());
  }
  
  @Override
  public void run() {
    System.out.println("in run method" + Thread.currentThread().getName());
    String str = Thread.currentThread().getName();
    for(int i = 0; i < 5; i++){
      // adding thread name to make element unique
      testMap.put(str+i, str+i);
      try {
        // delay to verify thread interference
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

输出:

in run methodThread-2
in run methodThread-0
in run methodThread-3
in run methodThread-1
Size of Map is 20