我正在创建基于Socket的Server-Client预订服务,并且有关于将被多个线程访问的类的问题,它是否需要扩展并发HashMap或者是否足以创建变量并发HashMap以确保线程安全?
我有两个想法,但我不确定第一个是否可行,所以第一个想法是创建只实现Serializable的类,该类具有可变日期,然后是线程想要操作的变量并发哈希映射,第二个想法是拥有扩展并发哈希映射的类,只是CHP,但带有附加变量以确保它与其他变量可区分
public class Day implements Serializable {
private LocalDate date;
private ConcurrentHashMap<String, Boolean> schedule;
public Day(LocalDate date){
this.date = date;
this.schedule = new ConcurrentHashMap<>();
IntStream.range(10, 18).forEachOrdered(
n -> this.schedule.put(LocalTime.of(n, 0).toString(), TRUE));
}
public void changeaval(String key,Boolean status) {
this.schedule.replace(key,status);
}
public boolean aval(String key){
return this.schedule.get(key);
}
public LocalDate getDate(){return this.date;}
public ConcurrentHashMap getSchedule(){return this.schedule;}
}
我只想拥有可以被多个线程访问并且可以与其他线程区分开来的Class/Object/可比较并且具有映射Int的并发HashMap-
在处理由多个线程访问的对象时,基本上需要注意两件事:
幸运的是,我们可以使用适当的同步来处理这两种情况。
让我们谈谈这个特殊的程序。
Localdate
本身是一个不可变且线程安全的类。如果我们查看这个类的源代码,我们会看到这个类的所有字段都是last
。这意味着一旦Localdate
的构造函数完成对象的初始化,对象本身将跨线程可见。但是当它被分配给不同对象中的引用变量时,赋值(换句话说,引用变量的内容)是否会对其他线程可见是我们需要注意的。
给定您的情况下的构造函数,我们可以确保字段date
跨线程的可见性,前提是date
是end
或易失性
。由于您没有修改类中的date
字段,因此您可以很好地将其设为最终字段,从而确保安全初始化。如果您后来决定为该字段设置一个setter方法(取决于您的业务逻辑和设计),您应该使字段易失性
而不是最终
。易失性
创建了一个发生前关系,这意味着在写入易失性
变量之前在特定线程中执行的任何指令一旦读取相同的易失性变量,其他线程就会立即看到。
ConCurrentHashMap
也是如此。您应该使字段计划
最终
。由于ConCurrentHashMap
本身具有所有必要的同步,因此当其他线程尝试读取它时,您针对键设置的任何值都将对其他线程可见。
但是请注意,如果您有一些可变对象作为ConCurrentHashMap
值而不是Boolean
,则必须以与上述相同的方式设计它。
此外,最好知道有一个称为搭载的概念,这意味着如果一个线程写入其所有字段,然后写入易失性
变量,则该线程在写入易失性
变量之前写入的所有内容都将对其他线程可见,前提是其他线程在第一个线程写入易失性
变量后首先读取该变量的值。但是当您这样做时,您必须非常小心地确保读写顺序,并且容易出错。所以,这是在你想从一段罕见的代码中挤出最后一滴性能时完成的。在性能之前有利于安全性、可运维性、易读性。
最后,代码中没有竞争条件。唯一正在发生的写入是在ConCurrentHashMap
上,它本身是线程安全的。
基本上,这两种方法是等价的。从架构的角度来看,在专用类中创建变量是首选的,因为可以更好地控制哪些方法可供用户访问。在扩展时,用户可以访问底层ConCurrentHashMap的许多方法并滥用它们。