通过单例访问HashMap



我有一个Record类,它扩展了一个DoctorRecord类。我希望线程将这些记录存储在一个散列映射中,我将其作为一个单例创建。我的问题是,每当一个线程向哈希图中添加一条记录,并且我试图在主方法中打印它的大小时(一旦所有线程都完成了执行),我总是得到一个0的大小,就好像没有进行任何修改一样。但是,当我在添加记录的方法中打印地图的大小时,它会显示正确的大小。我认为这是一个传递值机制的问题,但我似乎无法纠正这个问题。

哈希映射单例类:

public class MtlHashMap {
private static HashMap<Character, ArrayList<Record>> hmMtl = new HashMap<Character, ArrayList<Record>>();
public static HashMap<Character, ArrayList<Record>> getInstance(){
return hmMtl;
}
}

在重写的run()方法内部调用线程方法(哈希映射包含字母表中每个字母的一个列表)

public synchronized void createDRecord(String firstName, String lastName, String address, long phone, String specialization, String location){
System.out.println("Create D Record");
DoctorRecord d1 = new DoctorRecord(firstName, lastName, address, phone, specialization, location);
hm = MtlHashMap.getInstance();
Character ch = lastName.toLowerCase().charAt(0);
ArrayList<Record> list = (ArrayList<Record>) hm.get(ch);
if(list == null){
System.out.println("Create list");
list = new ArrayList<Record>();
}
list.add(d1);
System.out.println("Size of list " + ch + " is " + list.size());
hm.put(ch,list);
System.out.println("Size of " + location + "hash map is " + hm.size());
}

public class Main {

public static void main(String[] args) throws InterruptedException{
System.out.println("Main");
ManagerClient m1 = new ManagerClient("mtl");
ManagerClient m2 = new ManagerClient("mtl");
ManagerClient m3 = new ManagerClient("mtl");
ManagerClient m4 = new ManagerClient("mtl");

//Create Doctor Record
m1.run("Joana", "Sam", "272 Montpellier", 1231, "surgeon", "mtl");
m2.run("Joana", "Sam", "34 Lake", 1231, "surgeon", "mtl");
m4.run("Joana", "Sam", "34 Lake", 1231, "surgeon", "mtl");
m1.join();
m2.join();
m4.join();
System.out.println("Size of MTL hash map is: " + MtlHashMap.getInstance().size());
System.out.println("Size of LVL hash map is: " + LvlHashMap.getInstance().size());
System.out.println("Size of DDO hash map is: " + DdoHashMap.getInstance().size());

经理客户端类

public class ManagerClient extends Thread{ 
private String location;
private String id;
private static int managerIdCounter = 1000; //Maintains unique global IDs

public ManagerClient(String location){
this.location = location.toLowerCase();
this.id = location.toLowerCase()+managerIdCounter++;
}
public String getLocation(){
return (this.location).toLowerCase();
}
public String getmId(){
return this.id;
}
//Different run method overloads for each of the four methods needed,
//with the appropriate server being called using the locateServer() method

//Default run method never used, but must be overridden anyway
public void run(){
System.out.println("This should never appear");
}
//Create Doctor Record (5 String and 1 long argument) SYNCHRONIZE THIS FOR SAFE RUNNING
@SuppressWarnings("deprecation")
public synchronized void run(String firstName, String lastName, String address, long phone, String specialization, String location){
System.out.println("Manager " + this.getmId() + " creates a D record");
try{
System.setSecurityManager(new RMISecurityManager());
String path = "rmi://localhost:2020/"+getLocation();
ClinicServerInterface server = (ClinicServerInterface)Naming.lookup(path);
server.createDRecord(firstName, lastName, address, phone, specialization, location);
}catch(Exception e){
//e.printStackTrace();
}
}
//Create Nurse Record (6 String arguments)
public synchronized void run(String firstName, String lastName, String designation, String status, String statusDate){
System.out.println("Manager " + this.getmId() + " creates a N record");
try{
System.setSecurityManager(new RMISecurityManager());
String path = "rmi://localhost:2020/"+getLocation();
ClinicServerInterface server = (ClinicServerInterface)Naming.lookup(path);
server.createNRecord(firstName, lastName, designation, status, statusDate, getLocation());
}catch(Exception e){
//e.printStackTrace();
}       
}
//Get Record Counts (1 int argument)
public void run(int type){
String location = this.location;
}
//Edit Record (3 String arguments)
public void run(String recrodID, String fieldName, String newValue){
String location = this.location;
}

}

我不知道你的哈希图出了什么问题,但你的程序不会创建任何线程。您的main()例程执行以下操作:

public static void main(String[] args) throws InterruptedException {
...
ManagerClient m1 = new ManagerClient("mtl");
...
m1.run("Joana", "Sam", "272 Montpellier", 1231, "surgeon", "mtl");
...
m1.join();
...
}

这不会创建线程。它创建了一个ManagerClient对象,而ManagerClient对象是一种Thread对象;但是CCD_ 4不是线程

线程是一个执行代码的操作系统对象,Thread对象是一个Java对象,程序使用它来创建和管理螺纹的生命周期。在程序调用m1.start()之前,不会创建线程


如果您将程序更改为调用m1.start(),将会发生以下情况:您的程序将打印This should never appear

这是因为ManagerClient类覆盖Thread.run()如下:

//Default run method never used, but must be overridden anyway
public void run(){
System.out.println("This should never appear");
}

ManagerClient类还定义了一些名为"run"的其他方法。例如:

public synchronized void run(String firstName, String lastName, String address, long phone, String specialization, String location)

但这是一种不同的方法。如果程序调用m1.start()来启动新线程,则新线程将调用m1.run()NOTm1.run("Joana", "Sam", "272 Montpellier", ...)

你无法改变这一点。这就是Thread.start()的工作原理。

将参数传递给显式创建并启动的线程的正常方法是通过构造函数传递参数。例如。;

ManagerClient m1 = new ManagerClient("Joana", "Sam", "272 Montpillier", ...);
m1.start();

但是您可能需要重新考虑以这种方式创建和启动线程。

您的run(...)方法每个只做一件事,然后返回。线程run()方法返回时死亡。创建并杀死线程来执行简单的任务是个坏主意。这并不是说它会在像你的作业这样的玩具程序中造成任何伤害,但在现实世界的软件中,你应该使用的好主意被称为线程池

您可以通过阅读ExecutorServiceThreadPoolExecutorExecutors.newFixedThreadPool()来了解线程池。并且,通过查看Java并发教程(http://docs.oracle.com/javase/tutorial/essential/concurrency/)。

正如HashMap:的Javadoc中所说

请注意,此实现不是同步的。如果多个线程同时访问一个哈希映射,并且至少有一个线程在结构上修改了该映射,则必须对其进行外部同步。

但是,他抗议说,方法synchronized。是的,是的,但没有用。

如果public synchronized void createDRecord(...)ManagerClient类中的一个方法,那么您正在同步的监视器就是ManagerClient实例,您有其中的4个实例。因此,当监视器被获取和释放时,没有其他东西与它们竞争,所以就好像没有同步一样。

您应该在方法中使用synchronized块来锁定哈希映射本身:

public void createDRecord(...) {
HashMap<Character, ArrayList<Record>> hm = MtlHashMap.getInstance();
synchronized (hm) {
// Mutually exclusive access to hm goes here.
}
}

或者使用ConcurrentHashMap,就像@MickMnemonic在他的评论中建议的那样。


您还应该制作静态哈希映射final,以确保其初始化值在所有线程中都可见:

private static final HashMap<Character, ArrayList<Record>> hmMtl = new HashMap<>();
^

最新更新