在不同的线程中更新(或/和阅读)共享对象的不同字段是并发线程安全的吗?如果是,这是一个好的实践吗?
注意:假设我们知道在任何时候没有两个(或更多)线程会对此共享可变对象的同一字段/属性进行操作(读或写)。
例如,考虑以下代码-在线程t1
、t2
和t3
中同时更新和阅读共享可变对象emp
的不同字段是线程安全的吗?
package concurrency;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class SharedObjectApp {
Employee emp = new Employee(001, 100000, 32);
public static void main(String args[]){
new App().doSomething();
}
public static class App{
Employee emp = new Employee(001, 100000, 32);
public void doSomething(){
Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
emp.setSalary(emp.getSalary() + 1000);
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
emp.setExp(emp.getExp() + 1);
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 10; i ++){
System.out.println(emp.getId());
}
}
});
// is this thread safe?
t1.start(); //updating salary
t2.start(); //updating experience
t3.start(); //getting id
}
}
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public static class Employee {
int id;
double salary;
double exp;
}
}
1条答案
按热度按时间ozxc1zmp1#
这不是未定义的行为,但是它会减慢你的程序。同时写入和/或读取不同的字段(知道它们不依赖于彼此)是相当安全的。尽管这是一个教科书上的错误共享的例子,不应该这样做。
当访问一个内存位置(类和它的成员)时,该位置被复制到CPU缓存行中以加快访问速度。如果多个线程试图同时访问该内存位置,它可以使这些缓存行无效以保持内存一致性,从而降低执行速度。
From oracle docs:
然而,来自不同处理器的同一高速缓存行中的各个元素的同时更新使整个高速缓存行无效,即使这些更新在逻辑上彼此独立。高速缓存线的单个元素的每次更新都将该线标记为无效。访问同一线中的不同元素的其它处理器看到标记为无效的线。即使所访问的元素尚未被修改,它们也被迫从存储器或其它地方获取该行的更新的副本。这是因为高速缓存一致性是基于高速缓存行来维持的,而不是针对单个元素。结果,互连业务和开销将增加。此外,当高速缓存行更新正在进行时,禁止对行中的元素的访问。
Jenkov has great resource on this(他们谈论的是多CPU系统,但它仍然适用,因为平台线程共享缓存行)。