Синхронизированные потоки и блокировки - PullRequest
8 голосов
/ 24 июля 2011

Может ли кто-нибудь объяснить разницу между этими двумя примерами в контексте блокировки объектов:

public void method1(){
    synchronized(this){
        ....
    }
}

А

StringBuffer aStringBufferObject = new StringBuffer("A");

public void method2(){
    synchronized(aStringBufferObject){
        ....
    }
}

Я знаю, что первый пример получит блокировку экземпляра this, а второй получит блокировку экземпляра aStringBufferObject. Но я действительно не понимаю, каков эффект или различие между ними.

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

Я знаю, что синхронизация метода или блока кода не позволяет нескольким потокам одновременно получать доступ к этому блоку / методу, но какова цель указания объекта для блокировки и какова разница в способе объекта указано как в приведенных выше примерах?

Ответы [ 3 ]

8 голосов
/ 24 июля 2011

Какова цель указания объекта для блокировки?

Часто синхронизировать проще на this или Class (для статических методов). Но есть случаи, когда вам нужно будет синхронизировать определенный объект вместо неявной блокировки (this). К таким случаям относятся:

  • Вы хотите синхронизировать доступ к примитивам без использования this. Вы можете синхронизировать только на Object s, поскольку каждый Object связан с неявным монитором в Java. Примитивы не имеют таких неявных мониторов, и поэтому вам нужно использовать объект блокировки. Использование классов-обёрток плохой и неправильный выбор, особенно если вы в итоге измените объект блокировки в защищенном блоке .
  • Вы хотите выполнить синхронизацию на объекте, который фактически защищает критическую секцию, когда синхронизация на this не будет гарантировать безопасность потока. Например, если вы синхронизируете доступ к экземпляру ArrayList, совместно используемому для экземпляров класса A, то синхронизация для экземпляра A бесполезна. Поток может создать новый экземпляр A и получить доступ к списку, в то время как другой поток изменяет его. Если вы используете другую блокировку, за которую должны бороться все потоки, вы можете защитить список; эта блокировка может быть привязана к A.class, но это может быть любой объект, который будет предоставлять те же гарантии.
  • Вы хотите выполнить разбиение блокировок, чтобы гарантировать, что различные охраняемые блоки защищены разными блокировками вместо одной и той же блокировки. Другими словами, если потокобезопасно разрешать различным потокам получать разные блокировки для доступа к разным критическим секциям, тогда вы можете иметь разные блокировки для каждой критической секции.

Ниже приведен пример использования разделенной блокировки:

private Object method1Lock = new Object();
private Object method2Lock = new Object();

public void method1(){
    synchronized(method1Lock){
        ....
    }
}

public void method2(){
    synchronized(method2Lock){
        ....
    }
}

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


На ваш другой вопрос,

Например, во втором примере смогут ли потоки все еще выполнять код внутри синхронизированного блока, поскольку блокировка не связана с экземпляром this?

Во втором примере любой поток, входящий в защищенную область, должен получить блокировку, связанную с aStringBufferObject. Если другой поток удерживает эту блокировку, то текущий поток не будет продолжать дальше. Когда вы указываете this, тогда поток должен получить блокировку, связанную с текущим объектом. В обоих случаях поток должен получить блокировку; примеры отличаются только для объекта, который используется в качестве блокировки.

2 голосов
/ 24 июля 2011

Блок synchronized представляет собой монитор, который не содержит деталей для блокировки и разблокировки мьютекса.Поскольку каждый объект в Java имеет внутреннюю блокировку (см. Исходный код класса Object), при использовании оператора synchronized JVM поможет вам синхронизировать критическую секцию.Вы также можете синхронизировать блокировку самостоятельно, используя ReentrantLock в пакете java.util.concurrent.locks.

2 голосов
/ 24 июля 2011

Синхронизация на объекте означает, что другие блоки, которые синхронизируются на том же объекте, должны будут ждать.Например:

public void methodA() {
   synchronized(obj) {
      //Do one job
   }
}

public void methodB() {
   synchronized(obj) {
      //Do another job
   }
}

Если вы вызываете methodA() в одном потоке, а затем вызываете methodB() в другом потоке, methodB() не завершится до завершения methodA().

...