Concurrency in Java: synchronized static methods

2019-01-18 06:19发布

问题:

I want to understand how locking is done on static methods in Java.

let's say I have the following class:

class Foo {
    private static int bar = 0;
    public static synchronized void inc() { bar++; }
    public synchronized int get() { return bar; }

It's my understanding that when I call f.get(), the thread acquires the lock on the object f and when I do Foo.inc() the thread acquires the lock on the class Foo.

My question is how are the two calls synchronized in respect to each other? Is calling a static method also acquires a lock on all instantiations, or the other way around (which seems more reasonable)?


EDIT:

My question isn't exactly how static synchronized works, but how does static and non-static methods are synchronized with each other. i.e., I don't want two threads to simultaneously call both f.get() and Foo.inc(), but these methods acquire different locks. My question is how is this preventable and is it prevented in the above code.

回答1:

Static and instance synchronized methods are not related to each other, therefore you need to apply some additional synchronization between them, like this:

class Foo {
    private static int bar = 0;
    public static synchronized void inc() { bar++; }
    public synchronized int get() { 
        synchronized (Foo.class) { // Synchronizes with static synchronized methods
            return bar; 
        }
    }
}

(though in this case leaving synchronized on get() doesn't make sense, since it doesn't do anything that requires synchronization on instance).

Beware of deadlocks - since this code aquires multiple locks, it should do it in consistent order, i.e. other synchronized static methods shouldn't try to acquire instance locks.

Also note that this particular task can be solved without synchronization at all, using atomic fields:

class Foo {
    private static AtomicInteger bar = new AtomicInteger(0);
    public static void inc() { bar.getAndIncrement(); }
    public int get() { return bar.get(); }
}


回答2:

A synchronized static method is effectively equivalent to:

public static void foo() {
    synchronized (ClassName.class) {
        // Body
    }
}

In other words, it locks on the Class object associated with the class declaring the method.

From section 8.4.3.6 of the JLS:

A synchronized method acquires a monitor (§17.1) before it executes. For a class (static) method, the monitor associated with the Class object for the method's class is used. For an instance method, the monitor associated with this (the object for which the method was invoked) is used.



回答3:

Neither, the non-static synchronized call does not acquire a lock on the class itself. (And the static synchronized block does not lock any object instantiated from that class.)

In other words the calls f.get() (locks f) and Foo.inc() (locks the class Foo) can run concurrently. They are not "synchronized".

You could use a different pattern (singleton), or make all the methods static.



回答4:

If you read http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html.

It will tell you:

You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class.

which tells you all you need to know.



回答5:

Static locks are attached to the class definition and thus is shared between all instances of that class.

Synchronization of none static methods only apply to the current instance of the class (the lock is on the class instance, e.g., this). In your example you have two different locks with no interrelation.

I don't want two threads to simultaneously call both f.get() and Foo.inc(), but these methods acquire different locks. My question is how is this preventable and is it prevented in the above code

You must share a lock to be able to arbitrate access to both f.get and Foo.inc(). You can do this either by sharing the same static lock or by the same instance lock.



回答6:

These two calls do not synchronize in respect to each other. It is as you said, a caller of f.get() acquires the lock of f object and caller of Foo.inc() acquires Foo.class object's one. So the synchronization rules are the same as if instead of static call you called an instance synchronized method with another object.