Философия Java


Синхронизация счетчиков - часть 2


// produce the parameter values from args:

applet.isApplet = false; applet.numCounters = (args.length == 0 ? 12 : Integer.parseInt(args[0])); applet.numWatchers = (args.length < 2 ? 15 : Integer.parseInt(args[1])); Console.run(applet, 350, applet.numCounters * 50); } } ///:~

Можно заметить, что оба run() и synchTest() теперь synchronized.  Если синхронизировать только один из методов, то другой свободен в игнорировании блокировки объекта и может быть безнаказанно вызван. Это очень важное замечание: Каждый метод, который имеет доступ к критическим общим ресурсам должен быть synchronized, иначе он не будет правильно работать.

Теперь у программы появилось новое поведение. Watcher никогда не прочитает что происходит потому, что оба метода run() стали synchronized и, так как run() всегда запущен для каждого объекта, блокировка всегда установлена и synchTest() никогда не вызовется.  Это видно, так как accessCount никогда не меняется.

Что нам нравится в этом примере, так это возможность изолировать только часть кода внутри run(). Та часть кода, которую необходимо изолировать данным способ, называется  критическим участком (critical section) и используется ключевое слово synchronized, чтобы различными способами установить критические участки. Java поддерживает критические участки с помощью  синхронизированных блоков; в данном случае synchronized используется для определения объекта, блокировка которого будет использована для синхронизации прилагаемого кода:

synchronized(syncObject) {   // This code can be accessed    // by only one thread at a time }

До того как синхронизированный блок будет доступен, блокировка должна быть установлена в syncObject. Если какой-либо процесс уже имеет данную блокировку, то блок ( часть кода) не может быть доступен, пока не будет снята блокировка.

Пример Sharing2 может быть изменен если убрать ключевое слово synchronized у обоих методов run() и, вместо этого, установить блок synnchronized вокруг двух критических строк кода. Но что объект должен использовать как блокировку? То что уже используется synchTest(), т.е. ткущий объект (this)! Таким образом измененный run() выглядит следующим образом:

  public void run() {     while (true) {       synchronized(this) {         t1.setText(Integer.toString(count1++));         t2.setText(Integer.toString(count2++));       }       try {         sleep(500);       } catch(InterruptedException e) {         System.err.println("Interrupted");       }     }   }

Это единственные исправления которые необходимо сделать в Sharing2.java и, как видите, поскольку оба счетчика синхронизированы (согласно тому, что Watcher теперь может следить за ними), то Watcher получает соответствующий доступ во время выполнения run().

Конечно, вся синхронизация зависит от внимания программиста: каждый кусок кода который должен получать доступ к разделяемым ресурсам должен быть оформлен соответствующим синхронизированным блоком.




Начало  Назад  Вперед