Java 当映射被另一个线程使用时,重新分配对该映射的引用会引发异常吗?

Java 当映射被另一个线程使用时,重新分配对该映射的引用会引发异常吗?,java,multithreading,reference,Java,Multithreading,Reference,在下面的代码中,高吞吐量环境中的多个线程将使用静态方法getCustomerIdByClientKey 静态方法loadCustomers将每隔10分钟左右调用一次,以使缓存无效并加载新的客户集。可以添加或删除客户 我关心的行是clientKeyToCustomerId=newClientKeyToCustomerId 如果一个线程在重新分配时当前正在使用clientKeyToCustomerId映射,那么该线程会抛出异常,还是会在内存空间中继续运行而没有问题,还是会杀死整个JVM?:) 我不想

在下面的代码中,高吞吐量环境中的多个线程将使用静态方法
getCustomerIdByClientKey

静态方法
loadCustomers
将每隔10分钟左右调用一次,以使缓存无效并加载新的客户集。可以添加或删除客户

我关心的行是
clientKeyToCustomerId=newClientKeyToCustomerId

如果一个线程在重新分配时当前正在使用
clientKeyToCustomerId
映射,那么该线程会抛出异常,还是会在内存空间中继续运行而没有问题,还是会杀死整个JVM?:)

我不想同步访问映射的块,因为我认为这会对性能产生负面影响

我不想调用
map.clear()
,因为简单地说,访问查找的线程将在不应该返回空结果时返回空结果

如果更换地图会导致问题,您会采取什么方法来解决

package com.mycompany.key;

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

import com.mycompany.dao.CustomerDao;
import com.mycompany.model.Customer;

public class CustomerManager {

    private static Map<String, String> clientKeyToCustomerId = 
                   new HashMap<String, String>();

    public static void loadCustomers() {
        List<Customer> allCustomers = new CustomerDao().loadAll();
        Map<String, String> newClientKeyToCustomerId = new HashMap<String, String>();
        for (Customer customer : allCustomers) {
            newClientKeyToCustomerId.put(customer.getActiveKey1(),
                    customer.getCustomerId());
            newClientKeyToCustomerId.put(customer.getActiveKey2(),
                    customer.getCustomerId());
        }
        clientKeyToCustomerId = newClientKeyToCustomerId;
    }

    public static String getCustomerIdByClientKey(String pClientKey) {
        return clientKeyToCustomerId.get(pClientKey);
    }

}
package com.mycompany.key;
导入java.util.HashMap;
导入java.util.List;
导入java.util.Map;
进口com.mycompany.dao.CustomerDao;
导入com.mycompany.model.Customer;
公共类CustomerManager{
私有静态映射clientKeyToCustomerId=
新的HashMap();
公共静态void loadCustomers(){
List allCustomers=new CustomerDao().loadAll();
Map newClientKeyToCustomerId=新HashMap();
for(客户:所有客户){
newClientKeyToCustomerId.put(customer.getActiveKey1(),
getCustomerId());
newClientKeyToCustomerId.put(customer.getActiveKey2(),
getCustomerId());
}
clientKeyToCustomerId=newClientKeyToCustomerId;
}
公共静态字符串getCustomerIdByClientKey(字符串pClientKey){
返回clientKeyToCustomerId.get(pClientKey);
}
}

在被告知阅读volatile之后,我认为这个问题的公认答案解决了这个问题。我修改了下面的代码。volatile关键字停止本地缓存映射的任何线程

当前从旧地图读取的任何线程都将过时,但从业务角度来看,这是可以的

package com.mycompany.key;

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

import com.mycompany.dao.CustomerDao;
import com.mycompany.model.Customer;

public class CustomerManager {

    //private static Map<String, String> clientKeyToCustomerId = 
    // new HashMap<String, String>();
    private volatile static Map<String, String> clientKeyToCustomerId = 
               new HashMap<String, String>();

    public static void loadCustomers() {
        List<Customer> allCustomers = new CustomerDao().loadAll();
        Map<String, String> newClientKeyToCustomerId = new HashMap<String, String>();
        for (Customer customer : allCustomers) {
            newClientKeyToCustomerId.put(customer.getActiveKey1(),
                    customer.getCustomerId());
            newClientKeyToCustomerId.put(customer.getActiveKey2(),
                    customer.getCustomerId());
        }
        clientKeyToCustomerId = newClientKeyToCustomerId;
    }

    public static String getCustomerIdByClientKey(String pClientKey) {
        return clientKeyToCustomerId.get(pClientKey);
    }

}
package com.mycompany.key;
导入java.util.HashMap;
导入java.util.List;
导入java.util.Map;
进口com.mycompany.dao.CustomerDao;
导入com.mycompany.model.Customer;
公共类CustomerManager{
//私有静态映射clientKeyToCustomerId=
//新的HashMap();
私有易失性静态映射clientKeyToCustomerId=
新的HashMap();
公共静态void loadCustomers(){
List allCustomers=new CustomerDao().loadAll();
Map newClientKeyToCustomerId=新HashMap();
for(客户:所有客户){
newClientKeyToCustomerId.put(customer.getActiveKey1(),
getCustomerId());
newClientKeyToCustomerId.put(customer.getActiveKey2(),
getCustomerId());
}
clientKeyToCustomerId=newClientKeyToCustomerId;
}
公共静态字符串getCustomerIdByClientKey(字符串pClientKey){
返回clientKeyToCustomerId.get(pClientKey);
}
}

它当然不会终止线程,因为它具有以前的值
clientKeyToCustomerId
或更新的
newClientKeyToCustomerId
。只有坏的JVM故障才会导致JVM退出。问题是新线程是否会看到完全初始化的
newClientKeyToCustomerId
,因为您没有跨越任何内存障碍。实际上,您的其他线程可能根本看不到更新的映射,您至少需要使该变量变为易失性的。您确实应该考虑使用并发集合。volatile@Perception,我不认为我需要一个并发的集合,因为地图永远不会被写入(创建后),这是正确的还是我遗漏了一些微妙和重要的东西?