Avoid synchronized(this) in Java?

后端 未结 22 1212
予麋鹿
予麋鹿 2020-11-22 01:23

Whenever a question pops up on SO about Java synchronization, some people are very eager to point out that synchronized(this) should be avoided. Instead, they c

相关标签:
22条回答
  • 2020-11-22 01:48

    This is really just supplementary to the other answers, but if your main objection to using private objects for locking is that it clutters your class with fields that are not related to the business logic then Project Lombok has @Synchronized to generate the boilerplate at compile-time:

    @Synchronized
    public int foo() {
        return 0;
    }
    

    compiles to

    private final Object $lock = new Object[0];
    
    public int foo() {
        synchronized($lock) {
            return 0;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 01:49

    Well, firstly it should be pointed out that:

    public void blah() {
      synchronized (this) {
        // do stuff
      }
    }
    

    is semantically equivalent to:

    public synchronized void blah() {
      // do stuff
    }
    

    which is one reason not to use synchronized(this). You might argue that you can do stuff around the synchronized(this) block. The usual reason is to try and avoid having to do the synchronized check at all, which leads to all sorts of concurrency problems, specifically the double checked-locking problem, which just goes to show how difficult it can be to make a relatively simple check threadsafe.

    A private lock is a defensive mechanism, which is never a bad idea.

    Also, as you alluded to, private locks can control granularity. One set of operations on an object might be totally unrelated to another but synchronized(this) will mutually exclude access to all of them.

    synchronized(this) just really doesn't give you anything.

    0 讨论(0)
  • 2020-11-22 01:52

    While you are using synchronized(this) you are using the class instance as a lock itself. This means that while lock is acquired by thread 1, the thread 2 should wait.

    Suppose the following code:

    public void method1() {
        // do something ...
        synchronized(this) {
            a ++;      
        }
        // ................
    }
    
    
    public void method2() {
        // do something ...
        synchronized(this) {
            b ++;      
        }
        // ................
    }
    

    Method 1 modifying the variable a and method 2 modifying the variable b, the concurrent modification of the same variable by two threads should be avoided and it is. BUT while thread1 modifying a and thread2 modifying b it can be performed without any race condition.

    Unfortunately, the above code will not allow this since we are using the same reference for a lock; This means that threads even if they are not in a race condition should wait and obviously the code sacrifices concurrency of the program.

    The solution is to use 2 different locks for two different variables:

    public class Test {
    
        private Object lockA = new Object();
        private Object lockB = new Object();
    
        public void method1() {
            // do something ...
            synchronized(lockA) {
                a ++;      
            }
            // ................
        }
    
    
        public void method2() {
            // do something ...
            synchronized(lockB) {
                b ++;      
            }
            // ................
        }
    
    }
    

    The above example uses more fine grained locks (2 locks instead one (lockA and lockB for variables a and b respectively) and as a result allows better concurrency, on the other hand it became more complex than the first example ...

    0 讨论(0)
  • 2020-11-22 01:52

    The java.util.concurrent package has vastly reduced the complexity of my thread safe code. I only have anecdotal evidence to go on, but most work I have seen with synchronized(x) appears to be re-implementing a Lock, Semaphore, or Latch, but using the lower-level monitors.

    With this in mind, synchronizing using any of these mechanisms is analogous to synchronizing on an internal object, rather than leaking a lock. This is beneficial in that you have absolute certainty that you control the entry into the monitor by two or more threads.

    0 讨论(0)
  • 2020-11-22 01:53

    As already said here synchronized block can use user-defined variable as lock object, when synchronized function uses only "this". And of course you can manipulate with areas of your function which should be synchronized and so on.

    But everyone says that no difference between synchronized function and block which covers whole function using "this" as lock object. That is not true, difference is in byte code which will be generated in both situations. In case of synchronized block usage should be allocated local variable which holds reference to "this". And as result we will have a little bit larger size of function (not relevant if you have only few number of functions).

    More detailed explanation of the difference you can find here: http://www.artima.com/insidejvm/ed2/threadsynchP.html

    Also usage of synchronized block is not good due to following point of view:

    The synchronized keyword is very limited in one area: when exiting a synchronized block, all threads that are waiting for that lock must be unblocked, but only one of those threads gets to take the lock; all the others see that the lock is taken and go back to the blocked state. That's not just a lot of wasted processing cycles: often the context switch to unblock a thread also involves paging memory off the disk, and that's very, very, expensive.

    For more details in this area I would recommend you read this article: http://java.dzone.com/articles/synchronized-considered

    0 讨论(0)
  • 2020-11-22 01:54

    A good example for use synchronized(this).

    // add listener
    public final synchronized void addListener(IListener l) {listeners.add(l);}
    // remove listener
    public final synchronized void removeListener(IListener l) {listeners.remove(l);}
    // routine that raise events
    public void run() {
       // some code here...
       Set ls;
       synchronized(this) {
          ls = listeners.clone();
       }
       for (IListener l : ls) { l.processEvent(event); }
       // some code here...
    }
    

    As you can see here, we use synchronize on this to easy cooperate of lengthly (possibly infinite loop of run method) with some synchronized methods there.

    Of course it can be very easily rewritten with using synchronized on private field. But sometimes, when we already have some design with synchronized methods (i.e. legacy class, we derive from, synchronized(this) can be the only solution).

    0 讨论(0)
提交回复
热议问题