Synchronized static в Java

synchronized static (блокировка на уровне класса), а просто synchronized (блокировка на уровне объекта).

В коде ниже у нас уже будут 2 объекта ресурса одного класса. В них уже будет synchronized static блок.

В отличии от synchronized, блок synchronized static распространяется на все объекты одного класса.

То есть если поток дошел до synchronized static в одном объекте класса, то он блокирует этот кусок кода не только для других потоков, которые доходят до этого synchronized static в этом же объекте, а и для тех потоков, которые доходят до этого synchronized static в другом объекте того же класса, что и первый объект.

Пример программы:

public class StaticSinchExample { public static void main(String[] args) { // объект ресурса номер 1 CommonResource commonResource1 = new CommonResource(); // объект ресурса номер 2 CommonResource commonResource2 = new CommonResource(); for (int i = 1; i < 6; i++) { // запускаем 5 потоков, передаем в каждый из них // первый ресурс, общий для 5 потоков Thread t1 = new Thread(new CountThread(commonResource1)); // запускаем 5 потоков, передаем в каждый из них // второй ресурс, общий для 5 потоков Thread t2 = new Thread(new CountThread(commonResource2)); t1.setName("Thread t1_" + i); t2.setName("Thread t2_" + i); t1.start(); t2.start(); } // То есть, если один из 10 запускаемых выше потоков // дойдет до блока synchronized static в каком-либо // из объектов ресурсов, будь то commonResource1 // или commonResource2, то блок synchronized static // будет заблокирован для всех остальных потоков // во всех объектах ресурсов. } } class CommonResource { int x; static int x1; synchronized static void incrementstatic() { // Верхнюю строчку можно было бы переписать // как synchronized (CommonResource.class) – // блокирует для остальных потоков блок кода // класса, а не блок кода конкретного объекта. x1 = 1; for (int i = 1; i < 5; i++) { System.out.printf("Static %s %d \n", Thread.currentThread().getName(), x1); x1++; try { Thread.sleep(1100); } catch (InterruptedException e) {} } } synchronized void increment() { // Верхнюю строчку можно было бы переписать // как synchronized (this) – блокирует для // остальных потоков блок кода // конкретного объекта. x = 1; for (int i = 1; i < 5; i++) { System.out.printf("%s %d \n", Thread.currentThread().getName(), x); x++; try { Thread.sleep(100); } catch (InterruptedException e) {} } } } class CountThread implements Runnable { CommonResource res; CountThread(CommonResource res) { this.res = res; } public void run() { CommonResource.incrementstatic(); } }

Вывод:

Как видно, все 10 потоков выводятся по очереди.

То есть 4 значения одного потока из 10 потоков, потом 4 значения другого из 10 потоков и так до десятого потока.

Что значит, что блокировка synchronized static распространяется на все объекты ресурсов.


Детали о разнице synchronized static и просто synchronized.

synchronized static (на уровне класса) и просто synchronized (на уровне объекта) это ДВЕ РАЗНЫЕ БЛОКИРОВКИ. Могут выполняться параллельно друг другу.

Один sinchronized метод блокирует доступ и к другим synchronized методам в объекте. То есть если поток зашел в один synchronized метод объекта, то заблокирован для других потоков не только этот synchronized метод в этом объекте, заблокированы для других потоков и другие synchronized методы в этом объекте пока поток не покинет synchronized метод в объекте.

Если же один поток зашел в просто synchronized метод (не static) в объекте, то другие потоки могут спокойно заходить в synchronized static методы в этом объекте не дожидаясь пока поток, который зашел в просто synchronized выйдет оттуда.

То есть просто syncronized метод в объекте и synchronized static метод в этом объекте могут выполняться параллельно друг другу.

Пример программы:

public class StaticSinchExample2 { public static void main(String[] args) { //Объект ресурса CommonResource commonResource = new CommonResource(); for (int i = 1; i < 6; i++) { //запускаем 5 потоков и передаем //в каждый из них ресурс общий для потоков Thread t = new Thread(new CountThread(commonResource)); t.setName("Thread " + i); t.start(); } } } class CommonResource { int x; static int x1; synchronized void increment() { x = 1; for (int i = 1; i < 5; i++) { System.out.printf("%s %d \n", Thread.currentThread().getName(), x); x++; try { Thread.sleep(250); } catch (InterruptedException e) {} } } synchronized static void incrementstatic() { x1 = 1; for (int i = 1; i < 5; i++) { System.out.printf("Static %s %d \n", Thread.currentThread().getName(), x1); x1++; try { Thread.sleep(700); } catch (InterruptedException e) {} } } } //Код synchronized метода и synchronized static метода //не должны пересекаться так как блоки synchronized static //и просто synchronized могут выполняться параллельно, //а если они могут выполняться параллельно значит //они не синхронизированы между собой. class CountThread implements Runnable { CommonResource res; CountThread(CommonResource res) { this.res = res; } public void run() { //один поток может выполнять incrementstatic метод CommonResource.incrementstatic(); //другой поток может в это же время //параллельно выполнять increment res.increment(); } }

Вывод:

Как видно, после выхода Thread 1 из incrementstatic метода Thread 1 входит в increment метод и выполнение этого метода, как видно, происходит параллельно потоку Thread 5, который зашел в incrementstatic метод как только из него вышел Thread 1.

То есть очевидно synchronized static блок и synchronized блок выполняются параллельно друг другу.

Синхронизация с помощью Wait/Notify

Узнайте, как использовать wait/notify в Java для приостановки и возобновления потоков. Примеры кода и объяснение работы механизма межпоточной синхронизации.
Time to read: 9

Метод yield в Java

Метод yield() в Java: зачем поток уступает свое время другим? Разбор работы планировщика потоков и практическое применение с примером.
Time to read: 10

Semaphore в Java

Semaphore в Java: контроль доступа потоков к ресурсам. Как ограничить число одновременных операций и управлять очередью. Рабочий пример с кодом.
Time to read: 10