Когда мы запускаем два или более потоков в программе, может возникнуть ситуация, когда несколько потоков пытаются получить доступ к одному и тому же ресурсу и в итоге они могут дать непредвиденный результат из-за проблем с параллелизмом. Например, если несколько потоков пытаются выполнить запись в один и тот же файл, они могут повредить данные, потому что один из потоков может переопределить данные или когда один поток открывает файл, другой поток может закрывать тот же файл.
Таким образом, необходимо синхронизировать действие нескольких потоков и убедиться, что только один поток может получить доступ к ресурсу в заданный момент времени. Это реализовано с помощью концепции, называемой мониторами. Каждый объект в Java связан с монитором, который поток может блокировать или разблокировать. Только один поток одновременно может удерживать блокировку на мониторе.
Язык программирования Java предоставляет очень удобный способ создания потоков и синхронизации их задач с помощью синхронизированных блоков. Вы храните общие ресурсы в этом блоке. Ниже приводится общая форма синхронизированного оператора.
Синтаксис
synchronized(objectidentifier) {
// Доступ к общим переменным и другим общим ресурсам
}
Здесь objectidentifier – это ссылка на объект, блокировка которого связана с монитором, который представляет синхронизированный оператор. Теперь мы рассмотрим два примера, в которых мы выведем счетчик, используя два разных потока. Когда потоки не синхронизированы в Java, они выводят значение счетчика, которое не находится в последовательности, но если мы выводим счетчик, помещая внутрь блока synchronized(), он выводит счетчик последовательно для обоих потоков.
Пример многопоточности без синхронизации
Вот простой пример, который может или не может выводить значение счетчика последовательно, и каждый раз, когда мы его запускаем, он дает другой результат в зависимости от доступности процессора для потока.
Пример
class PrintDemo {
public void printCount() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Счетчик --- " + i );
}
} catch (Exception e) {
System.out.println("Поток прерван.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd) {
threadName = name;
PD = pd;
}
public void run() {
PD.printCount();
System.out.println("Поток " + threadName + " завершается.");
}
public void start () {
System.out.println("Запуск " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Поток - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Поток - 2 ", PD );
T1.start();
T2.start();
// ждем, пока потоки завершатся
try {
T1.join();
T2.join();
} catch ( Exception e) {
System.out.println("Прерван");
}
}
}
Это дает разные результаты каждый раз, когда вы запускаете эту программу:
Запуск Поток - 1
Запуск Поток - 2
Счетчик --- 5
Счетчик --- 4
Счетчик --- 3
Счетчик --- 5
Счетчик --- 2
Счетчик --- 1
Счетчик --- 4
Поток Поток - 1 завершается.
Счетчик --- 3
Счетчик --- 2
Счетчик --- 1
Поток Поток - 2 завершается.
Пример многопоточности с синхронизацией
Вот тот же пример, который последовательно выводит значение счетчика, и каждый раз, когда мы его запускаем, он дает одинаковый результат.
Пример
class PrintDemo {
public void printCount() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Счетчик --- " + i );
}
} catch (Exception e) {
System.out.println("Поток прерван.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd) {
threadName = name;
PD = pd;
}
public void run() {
synchronized(PD) {
PD.printCount();
}
System.out.println("Поток " + threadName + " завершается.");
}
public void start () {
System.out.println("Запуск " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Поток - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Поток - 2 ", PD );
T1.start();
T2.start();
// ждем, пока потоки завершатся
try {
T1.join();
T2.join();
} catch ( Exception e) {
System.out.println("Прерван");
}
}
}
Это дает один и тот же результат каждый раз, когда вы запускаете эту программу:
Запуск Поток - 1
Запуск Поток - 2
Счетчик --- 5
Счетчик --- 4
Счетчик --- 3
Счетчик --- 2
Счетчик --- 1
Поток Поток - 1 завершается.
Счетчик --- 5
Счетчик --- 4
Счетчик --- 3
Счетчик --- 2
Счетчик --- 1
Поток Поток - 2 завершается.
Оглавление
- 1. Java – Самоучитель для начинающих
- 2. Java – Обзор языка
- 3. Java – Установка и настройка
- 4. Java – Синтаксис
- 5. Java – Классы и объекты
- 6. Java – Конструкторы
- 7. Java – Типы данных и литералы
- 8. Java – Типы переменных
- 9. Java – Модификаторы
- 10. Java – Операторы
- 11. Java – Циклы и операторы цикла
- 11.1. Java – Цикл while
- 11.2. Java – Цикл for
- 11.3. Java – Улучшенный цикл for
- 11.4. Java – Цикл do..while
- 11.5. Java – Оператор break
- 11.6. Java – Оператор continue
- 12. Java – Операторы принятия решений
- 12.1. Java – Оператор if
- 12.2. Java – Оператор if..else
- 12.3. Java – Вложенный оператор if
- 12.4. Java – Оператор switch..case
- 12.5. Java – Условный оператор (? :)
- 13. Java – Числа
- 13.1. Java – Методы byteValue(), shortValue(), intValue(), longValue(), floatValue(), doubleValue()
- 13.2. Java – Метод compareTo()
- 13.3. Java – Метод equals()
- 13.4. Java – Метод valueOf()
- 13.5. Java – Метод toString()
- 13.6. Java – Метод parseInt()
- 13.7. Java – Метод Math.abs()
- 13.8. Java – Метод Math.ceil()
- 13.9. Java – Метод Math.floor()
- 13.10. Java – Метод Math.rint()
- 13.11. Java – Метод Math.round()
- 13.12. Java – Метод Math.min()
- 13.13. Java – Метод Math.max()
- 13.14. Java – Метод Math.exp()
- 13.15. Java – Метод Math.log()
- 13.16. Java – Метод Math.pow()
- 13.17. Java – Метод Math.sqrt()
- 13.18. Java – Метод Math.sin()
- 13.19. Java – Метод Math.cos()
- 13.20. Java – Метод Math.tan()
- 13.21. Java – Метод Math.asin()
- 13.22. Java – Метод Math.acos()
- 13.23. Java – Метод Math.atan()
- 13.24. Java – Метод Math.atan2()
- 13.25. Java – Метод Math.toDegrees()
- 13.26. Java – Метод Math.toRadians()
- 13.27. Java – Метод Math.random()
- 14. Java – Символы
- 14.1. Java – Метод Character.isLetter()
- 14.2. Java – Метод Character.isDigit()
- 14.3. Java – Метод Character.isWhitespace()
- 14.4. Java – Метод Character.isUpperCase()
- 14.5. Java – Метод Character.isLowerCase()
- 14.6. Java – Метод Character.toUpperCase()
- 14.7. Java – Метод Character.toLowerCase()
- 14.8. Java – Метод Character.toString()
- 15. Java – Строки
- 15.1. Java – Метод charAt()
- 15.2. Java – Метод compareTo()
- 15.3. Java – Метод compareToIgnoreCase()
- 15.4. Java – Метод concat()
- 15.5. Java – Метод contentEquals()
- 15.6. Java – Метод copyValueOf()
- 15.7. Java – Метод endsWith()
- 15.8. Java – Метод equals()
- 15.9. Java – Метод equalsIgnoreCase()
- 15.10. Java – Метод getBytes()
- 15.11. Java – Метод getChars()
- 15.12. Java – Метод hashCode()
- 15.13. Java – Метод indexOf()
- 15.14. Java – Метод intern()
- 15.15. Java – Метод lastIndexOf()
- 15.16. Java – Метод length()
- 15.17. Java – Метод matches()
- 15.18. Java – Метод regionMatches()
- 15.19. Java – Метод replace()
- 15.20. Java – Метод replaceAll()
- 15.21. Java – Метод replaceFirst()
- 15.22. Java – Метод split()
- 15.23. Java – Метод startsWith()
- 15.24. Java – Метод subSequence()
- 15.25. Java – Метод substring()
- 15.26. Java – Метод toCharArray()
- 15.27. Java – Метод toLowerCase()
- 15.28. Java – Метод toString()
- 15.29. Java – Метод toUpperCase()
- 15.30. Java – Метод trim()
- 15.31. Java – Метод valueOf()
- 15.32. Java – Классы StringBuilder и StringBuffer
- 15.32.1. Java – Метод append()
- 15.32.2. Java – Метод reverse()
- 15.32.3. Java – Метод delete()
- 15.32.4. Java – Метод insert()
- 15.32.5. Java – Метод replace()
- 16. Java – Массивы
- 17. Java – Дата и время
- 18. Java – Регулярные выражения
- 19. Java – Методы
- 20. Java – Потоки ввода/вывода, файлы и каталоги
- 20.1. Java – Класс ByteArrayInputStream
- 20.2. Java – Класс DataInputStream
- 20.3. Java – Класс ByteArrayOutputStream
- 20.4. Java – Класс DataOutputStream
- 20.5. Java – Класс File
- 20.6. Java – Класс FileReader
- 20.7. Java – Класс FileWriter
- 21. Java – Исключения
- 21.1. Java – Встроенные исключения
- 22. Java – Вложенные и внутренние классы
- 23. Java – Наследование
- 24. Java – Переопределение
- 25. Java – Полиморфизм
- 26. Java – Абстракция
- 27. Java – Инкапсуляция
- 28. Java – Интерфейсы
- 29. Java – Пакеты
- 30. Java – Структуры данных
- 30.1. Java – Интерфейс Enumeration
- 30.2. Java – Класс BitSet
- 30.3. Java – Класс Vector
- 30.4. Java – Класс Stack
- 30.5. Java – Класс Dictionary
- 30.6. Java – Класс Hashtable
- 30.7. Java – Класс Properties
- 31. Java – Коллекции
- 31.1. Java – Интерфейс Collection
- 31.2. Java – Интерфейс List
- 31.3. Java – Интерфейс Set
- 31.4. Java – Интерфейс SortedSet
- 31.5. Java – Интерфейс Map
- 31.6. Java – Интерфейс Map.Entry
- 31.7. Java – Интерфейс SortedMap
- 31.8. Java – Класс LinkedList
- 31.9. Java – Класс ArrayList
- 31.10. Java – Класс HashSet
- 31.11. Java – Класс LinkedHashSet
- 31.12. Java – Класс TreeSet
- 31.13. Java – Класс HashMap
- 31.14. Java – Класс TreeMap
- 31.15. Java – Класс WeakHashMap
- 31.16. Java – Класс LinkedHashMap
- 31.17. Java – Класс IdentityHashMap
- 31.18. Java – Алгоритмы Collection
- 31.19. Java – Iterator и ListIterator
- 31.20. Java – Comparator
- 32. Java – Дженерики
- 33. Java – Сериализация
- 34. Java – Сеть
- 34.1. Java – Обработка URL
- 35. Java – Отправка Email
- 36. Java – Многопоточность
- 36.1. Java – Синхронизация потоков
- 36.2. Java – Межпоточная связь
- 36.3. Java – Взаимная блокировка потоков
- 36.4. Java – Управление потоками
- 37. Java – Основы работы с апплетами
- 38. Java – Javadoc