Java中的线程安全类,通过同步块

wax*_*wax 19 java concurrency multithreading thread-safety

假设我们有非常简单的Java类MyClass.

public class MyClass {
   private int number;

    public MyClass(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}
Run Code Online (Sandbox Code Playgroud)

构造具有某种状态的线程安全Java类有三种方法:

  1. 让它真正不变

    public class MyClass {
       private final int number;
    
       public MyClass(int number) {
        this.number = number;
       }
    
       public int getNumber() {
        return number;
       }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 制作场地number volatile.

    public class MyClass {
       private volatile int number;
    
       public MyClass(int number) {
        this.number = number;
       }
    
       public int getNumber() {
           return number;
       }
    
       public void setNumber(int number) {
           this.number = number;
       }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用synchronized块.这种方法的经典版本在实践中的Java Concurrency第4.3.5节中描述.有趣的是,它在本书的勘误表中提到的示例中有错误.

    public class MyClass {
       private int number;
    
       public MyClass(int number) {
           setNumber(number);
       }
    
       public synchronized int getNumber() {
           return number;
       }
    
       public synchronized void setNumber(int number) {
           this.number = number;
       }
    }
    
    Run Code Online (Sandbox Code Playgroud)

还有一个事实应该添加到讨论的背景中.在multhithreaded环境中,JVM可以自由地重新排序synchronized块之外的指令,保留逻辑序列,并 JVM指定的关系之前发生.它可能导致尚未正确构造的发布对象到另一个线程.

关于第三种情况,我有几个问题.

  1. 它是否等同于以下代码:

    public class MyClass {
       private int number;
    
       public MyClass(int number) {
           synchronized (this){
               this.number = number;
           }
       }
    
       public synchronized int getNumber() {
           return number;
       }
    
       public synchronized void setNumber(int number) {
           this.number = number;
       }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 是否会在第三种情况下阻止重新排序,或者JVM可能重新排序intstructions并因此在字段中发布具有默认值的对象number

  3. 如果第二个问题的答案是肯定的,那么我还有一个问题.

     public class MyClass {
       private int number;
    
       public MyClass(int number) {
           synchronized (new Object()){
               this.number = number;
           }
       }
    
       public synchronized int getNumber() {
           return number;
       }
    
       public synchronized void setNumber(int number) {
           this.number = number;
       }
    }
    
    Run Code Online (Sandbox Code Playgroud)

这种奇怪的外观synchronized (new Object())应该可以防止重新排序的影响.它会起作用吗?

需要明确的是,所有这些示例都没有任何实际应用.我只是好奇多线程的细微差别.

ysh*_*vit 8

synchronized(new Object())将不执行任何操作,因为同步仅适用于您同步的对象.因此,如果线程A同步oneObject,并且线程B同步anotherObject,则它们之间不会发生.因为我们可以知道没有其他线程会在new Object()你创建的那个上同步,所以这不会在任何其他线程之间建立先发生过.

关于你synchronzied在构造函数中,如果你的对象安全地发布到另一个线程,你不需要它; 如果不是这样的话,你可能会遇到麻烦.我刚刚在并发兴趣列表上问过这个问题,并且产生了一个有趣的线程.特别参见这封电子邮件,它指出即使您的构造函数已同步,在没有安全发布的情况下,另一个线程可能会在您的字段中看到默认值,并且此电子邮件(imho)将整个事物联系在一起.

  • 它确实做了一些事情(强制块中的所有内容在构造线程之后发生的任何事情之前完成,所以如果构造线程在构造函数返回后立即将引用传递给另一个线程,则不需要担心非 - 最终字段.)它不足以*覆盖*所有*并发危险. (2认同)
  • 在分配B周围的第一个线程内进行同步会强制它在设置参考C之前可见.同步块的内存一致性副作用不会与排除副作用相关联.无论其他线程是否争用对象监视器,它们仍然适用于在第一个线程中执行的操作. (2认同)