Должен ли синхронизированный метод быть внешним по отношению к классу, который его использует?

Сценарий 1.

Синхронизированный метод является частным и находится внутри класса, который реализует Runnable

Main.java

public class Main { 

Thread thread1 = new Thread(new MyRunnable); 

. . . 

} 

MyRunnable.java

public class MyRunnable implements Runnable { 

. . . 

private synchronized doSomething { 

} 

Сценарий 2.

Синхронизированный метод является общедоступным, статическим и находится в классе Main.

Main.java

  public class Main { 

    Thread thread1 = new Thread(new MyRunnable); 

    public synchronized static doSomething() { 

     }  

    } 

MyRunnable.java

   public class MyRunnable implements Runnable { 

    . . . 


}

Вопрос: какой из приведенных выше сценариев является правильным?

Я следую сценарию 2. Итак, у меня есть синхронизированный метод в классе Main. Это отлично работает. Когда я переместил этот синхронизированный метод в класс MyRunnable, я не увидел никакой разницы. И это странно. Я ожидал, что это потерпит неудачу. synchronized предотвращает доступ к этому методу одновременно.

Но если я приведу два экземпляра класса:

MyRunnable runnable1 = new MyRunnable();  
MyRunnable runnable2 = new MyRunnable();  

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

1 ответ

Решение

synchronized определяет поведение во время выполнения. Это ничего не делает во время компиляции.

synchronized метод заблокирован на экземпляре объекта (this).
static synchronized метод заблокирован на Class объекта.

Это поведение определено в JLS § 8.4.3.6. synchronized методы

Синхронизированный метод получает монитор (§17.1), прежде чем он выполняется.

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

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

Итак, в вашем сценарии 1 метод заблокирован для каждого отдельного экземпляра MyRunnable и в сценарии 2 метод заблокирован на Main.class объект (который является более или менее глобальным, то же самое, когда находится под тем же загрузчиком классов).

Учтите это в * Сценарии 1:

MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);

Здесь и t1, и t2 используют один и тот же экземпляр MyRunnable Это означает, что они не смогут выполнить doSomething() в параллели.

Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());

Здесь t1 и t2 могут выполнять doSomething() в случае MyRunnable они были даны в paralell, так как они фиксируются на экземпляре.

В Сценарии 2 оба потока не могут выполняться doSomething() в параллели, потому что они запираются Main.class и загружаются тем же ClassLoader,

Другие вопросы по тегам