我有一个Record类,它扩展了DoctorRecord类。我希望线程将这些记录存储在我创建为单例的哈希映射中。我的问题是每次线程将一条记录添加到哈希映射中并尝试在main方法中打印它的大小(一旦所有线程都完成执行),我总是得到一个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;
}
}
答案 0 :(得分:1)
我不知道您的哈希映射是什么,但您的程序不会创建任何线程。你的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
对象;但是Thread
不是主题。
线程是执行代码的操作系统对象,Thread
对象是程序用来创建管理生命周期的Java对象螺纹。在程序调用{{1}}之前,不会创建线程。
如果您更改程序以致电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");
}
类还定义了一些名为“run”的其他方法。 E.g:
ManagerClient
但这是不同的方法。如果您的程序调用{{1}}来启动新主题,则新主题将调用public synchronized void run(String firstName, String lastName, String address, long phone, String specialization, String location)
和 NOT m1.start()
。
你无能为力改变这一点。这就是m1.run()
的工作原理。
将参数传递给您明确创建并启动的线程的常规方法是将它们传递给构造函数。 E.g;
m1.run("Joana", "Sam", "272 Montpellier", ...)
但您可能想以这种方式重新考虑创建和启动线程。
你的Thread.start()
方法每个只做一件事然后返回。当ManagerClient m1 = new ManagerClient("Joana", "Sam", "272 Montpillier", ...);
m1.start();
方法返回时,线程消失。创建和杀死线程以执行简单任务是一个坏主意。并不是说它会对你的作业中的玩具程序造成任何伤害,但在现实世界的软件中,你应该使用的好主意被称为线程池。
您可以通过阅读run(...)
,run()
和ExecutorService
来了解线索池。并且,通过查看Java Concurrency Tutorial(http://docs.oracle.com/javase/tutorial/essential/concurrency/)。
答案 1 :(得分:0)
正如Javadoc of HashMap
中所述:
请注意,此实施未同步。如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,则必须在外部进行同步。
但是,他抗议,方法是 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<>();
^