简体   繁体   English

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

[英]Thread-safe class in Java by means of synchronized blocks

Let's say we have very simple Java class MyClass . 假设我们有非常简单的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;
    }
}

There are three ways to construct thread-safe Java class which has some state: 构造具有某种状态的线程安全Java类有三种方法:

  1. Make it truly immutable 让它真正不变

     public class MyClass { private final int number; public MyClass(int number) { this.number = number; } public int getNumber() { return number; } } 
  2. Make field number volatile . 使字段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; } } 
  3. Use a synchronized block. 使用synchronized块。 Classic version of this approach described in Chapter 4.3.5 of Java Concurrency in practice. 这种方法的经典版本在实践中的Java Concurrency第4.3.5章中描述。 And the funny thing about that it has an error in the example which is mentioned in a errata for this book. 有趣的是,它在本书的勘误表中提到的示例中有错误。

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

There is one more fact that should be added to the context of discussion. 还有一个事实应该添加到讨论的背景中。 In a multhithreaded environment JVM is free to reorder instructions outside of synchronized block preserving a logical sequence and happens-before relationships specified by JVM. 在多线程环境中,JVM可以自由地重新排序synchronized块之外的指令,保留逻辑序列,并 JVM指定的关系之前发生 It may cause publishing object which is not properly constructed yet to another thread. 它可能导致尚未正确构造的发布对象到另一个线程。

I've got a couple of questions regarding the third case. 关于第三种情况,我有几个问题。

  1. Will it be equivalent to a following piece of code: 它是否等同于以下代码:

     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; } } 
  2. Will a reordering be prevented in the third case or it possible for JVM to reorder intstructions and therefore publish object with default value in field number ? 是否会在第三种情况下阻止重新排序,或者JVM可能重新排序intstructions,从而在字段number发布具有默认值的对象?

  3. If an answer for the second question is yes than I have one more question. 如果第二个问题的答案是肯定的,那么我还有一个问题。

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

This strange-looking synchronized (new Object()) is supposed to prevent reordering effect. 这种奇怪的synchronized (new Object())应该可以防止重新排序的影响。 Will it work? 它会起作用吗?

Just to be clear, all these examples don't have any practical applications. 需要说明的是,所有这些例子都没有任何实际应用。 I'm just curious about nuances of multithreading. 我只是好奇多线程的细微差别。

synchronized(new Object()) will do nothing, since synchronization is only on the object you synchronize on. synchronized(new Object())将不执行任何操作,因为同步仅在您同步的对象上。 So if thread A synchronizes on oneObject , and thread B synchronizes on anotherObject , there is no happens-before between them. 因此,如果线程A在oneObject同步,而线程B在oneObject anotherObject同步,则它们之间不会发生。 Since we can know for a fact that no other thread will ever synchronize on the new Object() you create there, this won't establish a happens-before between any other thread. 因为我们可以知道没有其他线程会在你创建的new Object()上同步,所以这不会在任何其他线程之间建立先发生过。

Regarding your synchronzied in the constructor, if your object is safely published to another thread, you don't need it; 关于你在构造函数中的synchronzied ,如果你的对象安全地发布到另一个线程,你不需要它; and if it's not, you're probably in a mess of trouble as it is. 如果不是这样的话,你可能会遇到麻烦。 I asked this question on the concurrency-interest list a bit ago, and an interesting thread resulted . 我刚刚在并发兴趣列表上问过这个问题,并且产生了一个有趣的线程 See in particular this email , which points out that even with your constructor synchronized, in the absence of safe publication another thread could see default values in your fields, and this email which (imho) ties the whole thing together. 特别参见这封电子邮件 ,它指出即使您的构造函数已同步,在没有安全发布的情况下,另一个线程可能会在您的字段中看到默认值,并且此电子邮件 (imho)将整个事物联系在一起。

In question #3, synchronized(new Object()) is a no-op and will prevent nothing. 在问题#3中, synchronized(new Object())是一个无操作,并且不会阻止任何操作。 The compiler can determine that no other threads could possibly synchronize on that object (since nothing else can access the object.) This is an explicit example in Brian Goetz's paper " Java theory and practice: Synchronization optimizations in Mustang ". 编译器可以确定没有其他线程可能在该对象上同步(因为没有其他线程可以访问该对象。)这是Brian Goetz的论文“ Java理论与实践:Mustang中的同步优化 ”中的一个明确示例。

Even if you did need to synchronize in a constructor, and even if your synchronized(new Object()) block was useful - ie, you were synchronizing on a different long-lived object, since your other methods are synchronizing on this , you have visibility problems if you're not synchronizing on the same variable. 即使你确实需要在构造函数中进行同步,即使你的synchronized(new Object())有用的 - 也就是说,你正在同一个不同的长期对象,因为你的其他方法正在同步this ,你有如果您没有在同一个变量上进行同步,则可见性问题。 That is to say, you do indeed want your constructor to also use synchronized(this) . 也就是说,你确实希望你的构造函数也使用synchronized(this)

An aside: 旁边:

Synchronizing on this is considered poor form. this同步被认为是不良形式。 Instead, synchronize on some private final field. 相反,在某些私有最终字段上进行同步。 Callers may synchronize on your object, which could lead to a deadlock. 调用者可能会在您的对象上进行同步,这可能会导致死锁。 Consider the following: 考虑以下:

public class Foo
{
    private int value;
    public synchronized int getValue() { return value; }
    public synchronized void setValue(int value) { this.value = value; }
}

public class Bar
{
    public static void deadlock()
    {
        final Foo foo = new Foo();
        synchronized(foo)
        {
            Thread t = new Thread() { public void run() { foo.setValue(1); } };
            t.start();
            t.join();
        }
    }
}

It's not obvious to callers of the Foo class that this would deadlock. Foo类的调用者并不明白这会导致死锁。 Best to keep your locking semantics internal and private to your class. 最好将锁定语义保持为类内部和私有。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM