java - 在不同线程中同时更新(或/和读取)共享可变对象的不同字段

标签 java

在不同线程中同时更新(或/和读取)共享对象的不同字段,线程安全吗?如果是的话,这是一个好的做法吗?

注意:假设我们知道在任何时候都没有两个(或更多)线程会对此共享可变对象的同一字段/属性进行操作(读取或写入)。

E.G 考虑这段代码 - 正在更新和读取共享可变对象的不同字段 emp在线程中t1 , t2t3同时线程安全并且是一种可接受的做法?

package concurrency;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

public class SharedObjectApp {

  public static void main(String args[]){
      new App().doSomething();
  }

  public 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;
  }

}

最佳答案

这不是未定义的行为,但它会减慢程序的速度。同时写入和/或读取不同的字段(知道它们不相互依赖)是非常安全的。尽管这是虚假共享的教科书示例,不应该这样做。

当访问内存位置(类及其成员)时,该位置会被复制到 CPU 缓存行中,以便更快地访问。如果更多线程尝试同时访问该内存位置,则可能会使这些缓存行无效以保持内存一致性,从而减慢执行速度。

From oracle docs :

However, simultaneous updates of individual elements in the same cache line coming from different processors invalidates entire cache lines, even though these updates are logically independent of each other. Each update of an individual element of a cache line marks the line as invalid. Other processors accessing a different element in the same line see the line marked as invalid. They are forced to fetch a more recent copy of the line from memory or elsewhere, even though the element accessed has not been modified. This is because cache coherency is maintained on a cache-line basis, and not for individual elements. As a result there will be an increase in interconnect traffic and overhead. Also, while the cache-line update is in progress, access to the elements in the line is inhibited.

Jenkov has great resource on this (他们谈论的是多 CPU 系统,但它仍然适用,因为平台线程共享缓存行)。

关于java - 在不同线程中同时更新(或/和读取)共享可变对象的不同字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75022618/

相关文章:

java - 无法使用vertx创建文件

java - 使 Sprite 保持在顶部并位于屏幕 View 中

java - 无法检测舞台 LIBGDX 中 Actor 的着陆事件

java - 如何使用 Jersey 客户端使用 Facebook JSON

java - Gmail REST API : Using Google Credentials Without Impersonate

java - Java中的字符串初始化

java - 如何创建 Set<String> 数组?

java - 读取.class文件

java - ArrayList 和 LinkedList 哪个运行得更快?

Java Stream优雅解决方案,避免多次循环