Многопоточность в Java: синхронизированные статические методы

Я хочу понять, как блокировка выполняется на статических методах в Java.

предположим, у меня есть следующий класс:

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

насколько я понимаю, когда я звоню f.get() поток получает блокировку для объекта f и когда я делаю Foo.inc() поток получает блокировку на класс Foo.

мой вопрос в том, как синхронизируются два вызова относительно друг друга? Вызов статического метода также получает блокировку на всех экземпляры или наоборот (что кажется более разумным)?


EDIT:

мой вопрос не совсем как static synchronized работает, но как статические и нестатические методы синхронизируются друг с другом. т. е. я не хочу, чтобы два потока одновременно вызывали оба f.get() и Foo.inc(), но эти методы приобретают разные замки. Мой вопрос в том, как это можно предотвратить и предотвратить в приведенном выше коде.

6 ответов


статические и экземплярные synchronized методы не связаны друг с другом, поэтому вам нужно применить некоторую дополнительную синхронизацию между ними, например:

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; 
        }
    }
}

(хотя в данном случае уходя synchronized on get() не имеет смысла, так как он не делает ничего, что требует синхронизации на экземпляре).

остерегайтесь блокировок-поскольку этот код получает несколько блокировок, он должен делать это в согласованном порядке, т. е. другие синхронизированные статические методы не должны попробуйте получить блокировки экземпляров.

также обратите внимание, что эта конкретная задача может быть решена без синхронизации вообще, используя атомные поля:

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

синхронизированный статический метод эффективно эквивалентен:

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

другими словами, он блокируется на Class объект, связанный с классом, объявляющим метод.

С раздел 8.4.3.6 JLS:

синхронизированный метод получает монитор (§17.1) перед выполнением. Для метода класса (статического) используется монитор, связанный с объектом класса для класса метода. Для метода экземпляра, монитор связанные с этим (объект, для которого был вызван метод) используется.


кроме того, нестатический синхронизированный вызов не получает блокировку самого класса. (И статический синхронизированный блок не блокирует любой объект, созданный из этого класса.)

другими словами, звонки f.get() (locks f) и Foo.inc() (замки класс Foo) могут выполняться одновременно. Они не "синхронизированы".

вы можете использовать другой шаблон (singleton) или сделать все методы статическими.


Если Вы читаете http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html.

Он скажет вам:

вы можете задаться вопросом, что происходит, когда вызывается статический синхронизированный метод, поскольку статический метод связан с классом, а не с объектом. В этом случай, поток приобретает внутренняя блокировка объекта класса связано с классом. Таким образом, доступ для управления статическими полями класса по замок, который отличается от замок для любого экземпляра класса.

, который говорит вам все, что вам нужно знать.


Static замки прикреплены к class определение и, таким образом, разделяется между всеми экземплярами этого класс.

синхронизация none static методы применяются только к текущему экземпляру класса (блокировка на класс экземпляр, например, this). В вашем примере у вас есть два разных замка без взаимосвязи.

Я не хочу, чтобы два потока одновременно вызывали оба f.get () и Foo.inc (), но эти методы приобретайте разные замки. Мой вопрос в том, как это можно предотвратить и предотвратить в приведенном выше коде

вы должны поделитесь блокировкой, чтобы иметь возможность арбитражного доступа к обоим f.get и Foo.inc(). Вы можете сделать это, используя одну и ту же статическую блокировку или блокировку экземпляра.


эти два вызова не синхронизируются друг с другом. Это, как вы сказали, вызывающий f.get() приобретает замок f объект и вызывающий Foo.inc() приобретает Foo.class объект один. Таким образом, правила синхронизации такие же, как если бы вместо статического вызова вы вызвали метод синхронизации экземпляра с другим объектом.