Ява прошла долгий путь. Очень долгий путь. И это несет в себе весь «мусор» из ранних дизайнерских решений.
Одна вещь, о которой сожалели снова и снова, это то, что каждый объект (потенциально) содержит монитор . Это вряд ли когда-либо необходимо, и этот недостаток был исправлен, наконец, в Java 5, когда были представлены новые API параллелизма, такие как java.util.concurrent.locks.Lock
и его подтипы. С тех пор написание синхронизированного параллельного кода стало намного проще, чем раньше, когда у нас были только ключевое слово synchronized
и сложный для понимания механизм wait()
и notify()
:
Синхронизированный модификатор почти не используется
Оригинальный языковой дизайн, указанный для этих «удобных» модификаторов на методах:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
// These are the same: public synchronized void method() { ... } public void method() { synchronized ( this ) { ... } } // So are these: public static synchronized void method() { ... } public static void method() { synchronized (ClassOfMethod. class ) { ... } } |
Вы вряд ли захотите синхронизировать всю область метода, чтобы сохранить минимальное время синхронизации, а выделение метода каждый раз, когда вам нужна синхронизация, утомительно.
Кроме того, монитор нарушает инкапсуляцию. Каждый может синхронизироваться на вашем мониторе, если вы синхронизируете на this
или на всем class
. Вы, вероятно, не хотите этого, поэтому большинство людей, которые все еще работают с synchronized
ключевым словом, просто создадут явный частный объект блокировки, такой как:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
class SomeClass { private Object LOCK = new Object(); public void method() { ... synchronized (LOCK) { ... } ... } } |
Если это стандартный вариант использования для классических synchronized
блоков, нужен ли нам монитор для каждого объекта?
Синхронизировано в более современной версии Java
Если бы Java была разработана с учетом сегодняшних знаний о языке Java, мы бы не позволили использовать synchronized
для любого случайного объекта (включая строки или массивы):
1
2
3
4
|
// Wouldn't work synchronized ( "abc" ) { ... } |
Мы хотели бы ввести специальный интерфейс Synchronizable
маркера, который гарантирует, что разработчики будут иметь монитор. И synchronized
блок будет принимать только Synchronizable
аргументы:
1
2
3
4
5
|
Synchronizable lock = ... synchronized (lock) { ... } |
Это будет работать точно так же, как foreach или try-with-resources:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
Iterable<Object> iterable = ... // The type to the right of ":" must be Iterable for (Object o : iterable) { ... } // The assignment type must be AutoCloseable try (AutoCloseable closeable = ...) { ... } // The assignment type must be a functional interface Runnable runnable = () -> {}; |
Таким образом, для того, чтобы данная функция языка работала, язык Java накладывает ограничения на типы, которые используются в этом контексте. В случае foreach или try-with-resources требуется конкретный тип JDK. В случае лямбда-выражений требуется соответствующий структурный тип (который является довольно эзотерическим, но умным для Java).
К сожалению, по причинам обратной совместимости не будет добавлено никаких новых ограничений для synchronized
блоков. Или будет там? Было бы замечательно, и дополнительное предупреждение могло бы быть выдано, если тип не Synchronizable
. Это может позволить в течение нескольких будущих основных выпусков удалять мониторы из объектов, которые не обязательно должны быть синхронизируемыми.
По сути, это то, что язык C делал с мьютексами все время. Они особенная вещь. Не обычное дело.
Ссылка: | Если бы Java была разработана сегодня: синхронизируемый интерфейс от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ . |