Ключевое слово Java synchronized отмечает блок или метод как критическую секцию. Критическая секция — это секция, в которой одновременно выполняется один и только один поток, и поток удерживает блокировку для синхронизированной секции.
Ключевое слово synchronized помогает при написании параллельных частей приложений, чтобы защитить общие ресурсы внутри этого блока.
Ключевое слово synchronized можно использовать с –
- блок кода
- метод
1. Синхронизированный блок Java
1.1 Синтаксис
Общий синтаксис для записи синхронизированного блока выглядит следующим образом. Здесь lockObject — это ссылка на объект, блокировка которого связана с монитором, который представляют синхронизированные операторы.
synchronized( lockObject ){// synchronized statements}
1.2 Внутренняя работа
Когда поток хочет выполнить синхронизированные операторы внутри синхронизированного блока, он ДОЛЖЕН получить блокировку на мониторе lockObject. За один раз только один поток может получить монитор объекта блокировки. Поэтому все остальные потоки должны ждать, пока этот поток, в данный момент получивший блокировку, завершит свое выполнение.
Таким образом, ключевое слово synchronized гарантирует, что только один поток будет выполнять операторы синхронизированного блока одновременно, и тем самым предотвращает повреждение общих данных внутри блока несколькими потоками.
Имейте в виду, что если поток переведен в спящий режим(с помощью метода sleep()), то он не снимает блокировку. В это время сна ни один поток не будет выполнять синхронизированные операторы блока.
Синхронизация Java выдаст исключение NullPointerException, если объект блокировки, используемый в «synchronized(lock)», равен null.
1.3 Пример синхронизированного блока Java
Программа Java для демонстрации использования синхронизированного блока. В данном примере у нас есть MathClass с методом printNumbers(). Этот метод выведет числа, начиная с 1 до номера аргумента N.
Обратите внимание, что код в методе printNumbers() находится внутри синхронизированного блока.
public class MathClass{void printNumbers(int n) throws InterruptedException{synchronized(this){for(int i = 1; i <= n; i++){System.out.println(Thread.currentThread().getName() + " :: "+ i);Thread.sleep(500);}}}}
Я создал два потока, которые начинают выполнять метод printNumbers() одновременно. Из-за синхронизации блоков доступ разрешен только одному потоку, а другой поток должен ждать, пока первый поток не завершит работу.
public class Main{public static void main(String args[]){final MathClass mathClass = new MathClass();//first threadRunnable r = new Runnable(){public void run(){try {mathClass.printNumbers(3);} catch(InterruptedException e) {e.printStackTrace();}}};new Thread(r, "ONE").start();new Thread(r, "TWO").start();}}
Вывод программы.
ONE :: 1ONE :: 2ONE :: 3TWO :: 1TWO :: 2TWO :: 3
2. Синхронизированный метод Java
2.1 Синтаксис
Общий синтаксис для написания синхронизированного метода выглядит следующим образом. Здесь lockObject — это ссылка на объект, блокировка которого связана с монитором, который представляют синхронизированные операторы.
<access modifier> synchronized method( parameters ){// synchronized code}
2.2 Внутренняя работа
Подобно синхронизированному блоку, поток ДОЛЖЕН получить блокировку на связанном объекте монитора с синхронизированным методом. В случае синхронизированного метода, объект блокировки –
- Объект '.class' – если метод статический.
- Объект «this» — если метод не статический. «this» ссылается на ссылку на текущий объект, в котором вызывается синхронизированный метод.
Подробнее: Блокировка на уровне объекта против блокировки на уровне класса в Java
Ключевое слово Java synchronized по своей природе является реентерабельным. Это означает, что если синхронизированный метод вызывает другой синхронизированный метод, которому требуется та же блокировка, то текущий поток, удерживающий блокировку, может войти в этот метод, не получая блокировку.
2.3 Пример синхронизированного метода Java
Подобно примеру synchronized block, мы можем применить ключевое слово synchronized в методе printNumber(), и это сделает метод синхронизированным. Теперь, если мы снова запустим пример, мы получим аналогичный вывод.
public class MathClass{synchronized void printNumbers(int n) throws InterruptedException{for(int i = 1; i <= n; i++){System.out.println(Thread.currentThread().getName() + " :: "+ i);Thread.sleep(500);}}}
Вывод программы.
ONE :: 1ONE :: 2ONE :: 3TWO :: 1TWO :: 2TWO :: 3
Пишите мне свои вопросы в комментариях.