Java – the synchronization in the constructor makes it happen before
I have a question about how to ensure that objects are thread safe through the JAVA memory model
I've read a lot that it doesn't make sense to write synchronization scopes in constructors, but why not? Yes, indeed, as long as the objects in the construct are not shared between threads (it shouldn't be), no thread other than the construct object can reach any synchronized (this) {...}, so you don't need to create this range in the constructor to exclude them But the scope of synchronization is not just exclusion; They are also used to create pre - existing relationships JLS. seventeen point four
This is a sample code to make my point clear
public class Counter{ private int count; public Counter(int init_value){ //synchronized(this){ this.count = init_value; //} } public synchronized int getValue(){ return count; } public synchronized void addValue(){ count++; } }
Consider the case where thread t0 creates a counter object and another thread T1 uses it If there is a synchronized statement in the constructor, it is obviously guaranteed to be thread safe (because all operations in the synchronization scope have relationships with each other.) But if not, that is, there is no synchronization statement, does the JAVA memory model still ensure that T1 can see the initialization write count of t0? I don't think so Like FY in JLS The example code in 17.5 is the same as that in 17.5-1 With JSL 17.5-1 is different. Now the second thread only accesses this field from the synchronization method, but I think the synchronization statement is in this case (they will not create any relationship with any action before t0.) Some people say that at the end of the constructor, the rule about the edge before it happens guarantees it, but the rule seems to just say that the constructor happens - before finalize()
So should I write synchronized statements in constructors to make objects thread safe? Or are there any rules or logic about the Java Memory Model I missed that don't actually need it? If I'm true, even openjdk's hashtable (although I know it's outdated) doesn't seem thread safe
Or am I wrong about the definition of thread safety and concurrency policy? If I transfer the counter object from t0 to T1 in a thread safe way, for example, through a variable that is changeable, there seems to be no problem (in this case, the construction of t0 occurs before the volatile write, before the volatile read of T1, and before everything T1 does.) Should I always pass thread safe objects (but not immutable) in the thread in a way that leads to a previous relationship?
Solution
If the object is published safely (for example, by instantiating it as somevolatilefield = new foo()), you do not need to synchronize in the constructor If not, then synchronization in the constructor is not enough
On this issue, the discussion on the Java concurrency benefit list a few years back is a little lengthy; I will provide a summary here (full disclosure: I started the discussion and participated in it.)
Remember, before - before edge applies only to one thread that releases a lock and subsequent threads that acquire it So suppose you have:
someNonVolatileField = new Foo();
Here are three important sets of actions:
>The object being assigned has all fields set to 0 / null > the constructor is running, including get and release of the object monitor > assign a reference to the object to somenonvolatilefield
Suppose another thread uses a reference and calls the synchronized dofoo () method Now let's add two more actions:
>Read somenonvolatilefield reference > call dofoo (), which includes the acquisition and release of the object monitor
Since publishing to somenonvolatilefield is not secure, the system can perform a lot of reordering In particular, read threads are allowed to view events in the following order:
>All fields of the object being allocated are set to 0 / null > assign the reference of the object to somenonvolatilefield > read somenonvolatilefield reference > call dofoo(), including the acquisition and release of the object monitor > the constructor is running, including the acquisition and release of the object monitor
In this case, there is still one that happens at the front edge, but the reverse is what you want Specifically, the call to dofoo () takes place before the constructor
This will really bring you some gains; This means that any synchronized method (or block) can guarantee to see the complete effects of the constructor, or can't see these effects; It won't see only part of the constructor But in fact, you may want to ensure that you see the effect of the constructor; After all, that's why you write constructors
You can solve this problem by making dofoo () out of sync. Instead, set some rotation loops, wait for a flag indicating that the constructor has run, and then a manual synchronization (this) block But when you reach this level of complexity, it's best to just say "this object is thread safe, assuming its initial release is safe." This is the de facto assumption of most mutable classes, which call themselves thread safe; Immutable can use the final field. Even in the face of unsafe publishing, it is thread safe, but does not need explicit synchronization