Ява прошла долгий путь. Очень долгий путь. И это несет в себе весь «мусор» из ранних дизайнерских решений.
Одна вещь, о которой сожалели снова и снова, это то, что каждый объект (потенциально) содержит монитор . Это вряд ли когда-либо необходимо, и этот недостаток был исправлен, наконец, в 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 worksynchronized ("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 Iterablefor (Object o : iterable) { ...}// The assignment type must be AutoCloseabletry (AutoCloseable closeable = ...) { ...}// The assignment type must be a functional interfaceRunnable runnable = () -> {}; |
Таким образом, для того, чтобы данная функция языка работала, язык Java накладывает ограничения на типы, которые используются в этом контексте. В случае foreach или try-with-resources требуется конкретный тип JDK. В случае лямбда-выражений требуется соответствующий структурный тип (который является довольно эзотерическим, но умным для Java).
К сожалению, по причинам обратной совместимости не будет добавлено никаких новых ограничений для synchronized блоков. Или будет там? Было бы замечательно, и дополнительное предупреждение могло бы быть выдано, если тип не Synchronizable . Это может позволить в течение нескольких будущих основных выпусков удалять мониторы из объектов, которые не обязательно должны быть синхронизируемыми.
По сути, это то, что язык C делал с мьютексами все время. Они особенная вещь. Не обычное дело.
| Ссылка: | Если бы Java была разработана сегодня: синхронизируемый интерфейс от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ . |