Должен ли синхронизированный метод быть внешним по отношению к классу, который его использует?
Сценарий 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
,