Synchronized Block within Synchronized Method

前端 未结 4 592
一生所求
一生所求 2021-01-02 22:39

I\'m looking at some code in a third party library that contains a synchronized method, and within this method there is a synchronized block that locks on an instance variab

4条回答
  •  别那么骄傲
    2021-01-02 23:00

    In your example the method is both locking on the instance of Foo and on the object bar. Other methods may only be locking on the instance of Foo or on the object bar.

    So, yes, this makes sense depending on exactly what they are doing. Presumably bar protects some smaller subset of data, and some methods will only need to lock on bar to perform their actions in a thread-safe manner.

    What synchronized does

    synchronized (on a method, or in a statement) creates a mutual exclusion zone (critical section or, specifically for Java, a reentrant mutex). The "key" for a thread to enter the critical section is the object reference used in the synchronized statement . Only one thread can (recursively) "own" this "key" at one time across all blocks that use the same key; that is, only one thread can enter any block synchronized on a given object reference at one time.

    Such a critical section simply prevents the operations (variable read/write) that you do inside the block happening concurrently with any other operations in all other critical sections that lock on the same object reference. (It doesn't automatically protect all variables inside an object).

    In Java, such a critical section also creates a happens-before contract.

    By example

    As a somewhat contrived example :

    public class Foo {
        final Bar bar = new Bar();
        
        private int instanceCounter = 0;
        private int barCounter = 0;
    
        public synchronized void incrementBarCounterIfAllowed() {
            synchronized (bar) {
                if (instanceCounter < 10) barCounter++;
            }
        }
    
        public synchronized void incrementClassCounter() {
            instanceCounter++;
        }
    
        public void incrementBarCounter() {
            synchronized (bar) {
                barCounter++;
            }
        }
    
    }
    

    Whether the instance variables are private or not doesn't really matter to whether this approach is applicable. In a single class you can have multiple lock objects, each of which protect their own set of data.

    However the risk of doing this is that you have to be very strict with coding conventions to prevent deadlocks by locking two locks in different orders in different places. For example, with the above code if you then do this from somewhere else in the code:

    synchronized(myFoo.bar) {
      myFoo.incrementClassCounter();
    }
    

    you risk a deadlock with the incrementBarCounterIfAllowed() method

    Note that barCounter could be an instance variable for Bar etc etc - I avoided that for the sake of brevity in the code sample.

    In the case of synchronized methods, that reference is the reference to the class instance (or to the Class for the class for static methods).

提交回复
热议问题