What is the purpose of using synchronized (Thread.

2019-01-23 01:55发布

问题:

I faced the following code in our project:

synchronized (Thread.currentThread()){
    //some code
}

I don't understand the reason to use synchronized on currentThread.

Is there any difference between

synchronized (Thread.currentThread()){
    //some code
}

and just

//some code

Can you provide an example which shows the difference?

UPDATE

more in details this code as follows:

synchronized (Thread.currentThread()) {
       Thread.currentThread().wait(timeInterval);
}

It looks like just Thread.sleep(timeInterval). Is it truth?

回答1:

consider this

    Thread t = new Thread() {
        public void run() { // A
            synchronized (Thread.currentThread()) {
                System.out.println("A");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
        }
    };
    t.start();
    synchronized (t) { // B
        System.out.println("B");
        Thread.sleep(5000);
    }

blocks A and B cannot run simultaneously, so in the given test either "A" or "B" output will be delayed by 5 secs, which one will come first is undefined



回答2:

Although this is almost definitely an antipattern and should be solved differently, your immediate question still calls for an answer. If your entire codebase never acquires a lock on any Thread instance other than Thread.currentThread(), then indeed this lock will never be contended. However, if anywhere else you have

synchronized (someSpecificThreadInstance) { ... }

then such a block will have to contend with your shown block for the same lock. It may indeed happen that the thread reaching synchronized (Thread.currentThread()) must wait for some other thread to relinquish the lock.



回答3:

Basically there is no difference between the presence and absence of the synchronized block. However, I can think of a situation that could give other meaning to this usage.

The synchronized blocks has an interesting side-effect of causing a memory barrier to be created by the runtime before entering and after leaving the block. A memory barrier is a special instruction to the CPU that enforces all variables that are shared between multiple threads to return their latest values. Usually, a thread works with its own copy of a shared variable, and its value is visible to this thread only. A memory barrier instructs the thread to update the value in a way so that the change is visible to the other threads.

So, the synchronized block in this case does not do any locking (as there will be no real case of lock and wait situation, at lest none I can think of)(unless the use-case mentioned in this answer is addressed), but instead it enforces the values of the shared fields to return their latest value. This, however, is true if the other places of the code that work with the variables in question also uses memory barriers (like having the same synchronized block around the update/reassignment operations). Still, this is not a solution for avoiding race conditions.

If you're interested, I recommend you to read this article. It is about memory barriers and locking in C# and the .NET framework, but the problem is similar for Java and the JVM (except for the behavior of volatile fields). It helped me a lot in understanding how threads, volatile fields and locks work in general.

One must take into account some serious considerations in this approach, that were mentioned in comments below this answer.

  • The memory barrier does not imply locking. The access will still be non-synchronized and a subject to race conditions and other potential issues one may encounter. The only benefit is the thread being able to read the latest values of the shared memory fields, without the use of locks. Some practices use similar approaches if the working thread only reads from values and it does only care for them to be the most present ones, while avoiding the overhead of locks - a use case could be a high-performance simultaneous data processing algorithm.
  • The approach above is unreliable. As per Holger's comment, the compiler could eliminate the lock statements when optimizing, as it could consider them unnecessary. This will also remove the memory barriers. The code then will not issue a lock, and it will not work as expected if a lock was meant to be used, or the purpose was to create a memory barrier.
  • The approach above is also unreliable because the runtime JVM can remove synchronization when it can prove the monitor will never be acquired by another thread, which is true of this construct if the code never synchronizes on another thread object which is not the current thread's thread object. So even if it works during testing on system A, it might fail under another JVM on system B. Even worse, the code could work for a while and then cease working as optimizations are applied.
  • The intentions of the code as it stays now are ambiguous, so one should use more explicit and expressive means to achieve its effect (see Marko Topolnik's comment for reference).


回答4:

You are implementing a recursive mutex.

i.e. the same thread can enter the synchronisation block, but not other threads.