Как заблокировать метод для целого синхронизируемого использования класса?

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

Что относительно классов, как обеспечить блокировку на всем классе объектов, когда поток выполняет некоторый код на экземпляре того класса?

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

6
задан Joachim Sauer 28 June 2010 в 15:07
поделиться

8 ответов

Вы синхронизируете определенный объект, либо определенный объект статической блокировки, либо объект класса (что происходит, когда статические методы объявлены как синхронизируемые):

class X {
    private static final Object lock = new Object();
    public void oneAtATime() {
        synchronized (lock) {
            // Do stuff
        }
    }
}
class Y {
    public void oneAtATime() {
        synchronized (Y.class) {
            // Do stuff
        }
    }
}

Каждый вариант имеет свои плюсы и минусы; блокировка класса позволяет другому коду, за пределами класса, использовать ту же блокировку по своим причинам (что позволяет ему организовать более высокоуровневую синхронизацию, чем та, которую вы предоставляете), в то время как статическая финальная блокировка объекта подход позволяет вам запретить это, сделав поле блокировки закрытым (что упрощает рассуждение о блокировке и предотвращает взаимоблокировку вашего кода из-за того, что кто-то написал неправильный код).

Вы, конечно, также можете использовать некоторый механизм синхронизации из java.util.concurrent , например явные Lock s, которые обеспечивают больший контроль над блокировкой (и ReentrantLock ] в настоящее время работает немного лучше, чем неявные блокировки при высокой конкуренции).


Изменить: Обратите внимание, что статические / глобальные блокировки - не лучший способ - это означает, что каждый экземпляр когда-либо созданного класса будет по существу привязан ко всем другим экземплярам (что, помимо того, что затрудняет тестирование или прочитать код, может серьезно повредить масштабируемость). Я полагаю, вы делаете это для синхронизации какого-то глобального состояния? В этом случае я бы подумал о том, чтобы вместо этого обернуть это глобальное / статическое состояние в класс и реализовать синхронизацию для каждого экземпляра, а не глобально.

Вместо этого:

class Z {
    private static int state;
    public void oneAtATime(){
        synchronized (Z.class) {
            state++;
        }
    }
}

Сделайте это так:

class State {
    private int value;
    public synchronized void mutate(){ value++; }
}
class Z {
    private final State state;
    public Z(State state){
        this.state = state;
    }
    public void oneAtATime(){
        state.mutate();
    }
}
// Usage:
State s1 = new State(), s2 = new State();
Z foo = new Z(s1);
Z bar = new Z(s1);
Z frob = new Z(s2);
Z quux = new Z(s2);

Теперь foo и bar по-прежнему связаны друг с другом, но они могут работать независимо от frob и quux .

8
ответ дан 8 December 2019 в 12:57
поделиться

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

private static final Object STATIC_LOCK = new Object();

private void foo() {
     synchronized (STATIC_LOCK) {
          //do stuff...
     } 
}
5
ответ дан 8 December 2019 в 12:57
поделиться

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

Это довольно агрессивная синхронизация, я бы ее по возможности избегал.

2
ответ дан 8 December 2019 в 12:57
поделиться

Синхронизация по статическому полю вашего класса или по самому классу:

synchronized(MyClass.class) {
        // mutually excluded method body
}
2
ответ дан 8 December 2019 в 12:57
поделиться

Оба потока должны использовать эту конструкцию

public void someMethod() {
  synchronized(ClassThatShouldBeProtected.class) {
    someSynchronizedCode();
  }
}

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

1
ответ дан 8 December 2019 в 12:57
поделиться

Для этого нет встроенного механизма. Создайте свой собственный атрибут статической блокировки и убедитесь, что вы блокируете и разблокируете его в каждом методе. Не забывайте об исключениях - убедитесь, что вы разблокировали его в разделах «наконец».

0
ответ дан 8 December 2019 в 12:57
поделиться

http://www.janeg.ca/scjp/threads/synchronization.html

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

требуется ли блокировка рычага класса для доступа к статическим переменным класса или для защиты доступа к общему внешнему ресурсу класса? в этом случае вы должны иметь отдельную блокировку при доступе к нему.

0
ответ дан 8 December 2019 в 12:57
поделиться

Это должно сработать:

public class MyClass {
  void synchronizedMethod() {
    synchronized (MyClass.class) {
       // synchronized on static level
    }
  }
}

Которая "пропускает" runtime-представление класса для блокировки. Это возможно, поскольку в Java любой объект может быть использован в качестве мьютекса.

0
ответ дан 8 December 2019 в 12:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: