Бесплатный курс по Java: от основ до продвинутого уровня
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 в Java для приостановки и возобновления потоков. Примеры кода и объяснение работы механизма межпоточной синхронизации.
Time to read: 9
Метод yield в Java
Метод yield() в Java: зачем поток уступает свое время другим? Разбор работы планировщика потоков и практическое применение с примером.
Time to read: 10
Semaphore в Java
Semaphore в Java: контроль доступа потоков к ресурсам. Как ограничить число одновременных операций и управлять очередью. Рабочий пример с кодом.