Многопоточный параллелизм может быть достигнут в JAVA с помощью оператора Synchronized. Используя синхронизированные блоки кода, JVM гарантирует, что только один поток может одновременно удерживать блокировку определенного объекта. Механизм блокировки позволяет нескольким потокам безопасно получать доступ к критически важным ресурсам.
Код синхронизации записывается следующим образом:
Код 1:
Object obj = new Object(); ...syncd(obj) { //TODO: доступ к критически важным ресурсам} Многопоточность JAVA всегда полна ловушек. Если мы используем Boolean в качестве синхронизируемого объекта, могут возникнуть следующие две ситуации:
1. Думается, что один объект заблокирован, но на самом деле синхронизируются разные объекты.
Код 2:
частный изменчивый логический isTrue = false; publich void aMethod() { ... синхронизированный (isTrue) { isTrue = !isTrue; // TODO: доступ к критическим ресурсам isTrue = !isTrue } ... }; На первый взгляд, в приведенном выше коде нет ничего плохого. Благодаря использованию Synchronized(isTrue) только один поток может одновременно получить доступ к критическим ресурсам, но это не так. Потому что две константы false и true соответствуют двум разным объектам. При изменении isTrue вполне вероятно, что разные потоки будут синхронизировать разные объекты. Автоматическая упаковка JAVA изменит false на Boolean.FALSE и true на Boolean.TRUE (это также показывает, что если false изменить на Boolean.FALSE, результат будет таким же). Напишите тестовый код для одной из вышеперечисленных ситуаций следующим образом:
Код 3:
public class BooleanTest { Private volut Boolean isTrue = Boolean.FALSE // Здесь то же самое, что и false public void aMethod() { for(int i=0;i<10;i++) { Thread t = new Thread() { public void run() {synced(isTrue) { isTrue = !isTrue; System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue); try{ Double ran = 1000 * Math.random(); Thread.sleep(ran.intValue()); }catch(InterruptedException e) {} if(!isTrue) System.out.println( Thread.currentThread().getName() + " - О, нет!" isTrue = !isTrue(); t.start(); } } public static void main(String... args) { BooleanTest bt = new BooleanTest(); Когда вы запустите приведенный выше код, вы время от времени будете видеть сообщение «-Oh, No!», указывающее, что разные потоки входят в синхронизированный блок кода одновременно.
2. Думается, что синхронизированы разные объекты, но на самом деле это один объект.
Иногда нам может потребоваться синхронизировать несколько объектов. Если в качестве синхронизируемого объекта используется логическое значение, вполне вероятно, что два блока синхронизации, которые не должны иметь связи, используют одну и ту же блокировку объекта. Примеры следующие:
Код 4:
частный изменчивый логический aBoolean = Boolean.FALSE; частный изменчивый логический другойBoolean = false; ..synchroned(anotherBoolean) { //TODO: доступ к критическому ресурсу 2 } ... } Предположим, что первоначально aMethod иotherMethod будут вызываться двумя наборами несвязанных потоков. Однако, поскольку Boolean.FALSE и false указывают на один и тот же объект, доступ к критическому ресурсу 2 может быть заблокирован критическим ресурсом 1 (и наоборот).
Две приведенные выше ситуации указывают на то, что при использовании синхронизированных блоков старайтесь не использовать логические объекты в качестве синхронизированных объектов, иначе могут возникнуть непредвиденные проблемы или могут возникнуть ловушки при будущих модификациях кода.
Из этого также видно, что любая синхронизация констант рискованна. Если вам необходимо синхронизировать логическое значение, вы должны использовать оператор new для создания логического объекта.