JavaRush /Курси /Модуль 2. Java Core /Проблеми багатопотоковості: звернення до спільного ресурс...

Проблеми багатопотоковості: звернення до спільного ресурсу

Модуль 2. Java Core
Рівень 12 , Лекція 0
Відкрита

— Привіт, Аміго! Ми вже обговорили переваги та зручності, які несе з собою багатопотоковість (multithreading). Тепер час подивитись і на недоліки. Вони, на жаль, відчутні.

Раніше ми дивились на програму як на набір об'єктів, що викликають методи один одного. Тепер все стало трохи складніше. Програма – це радше набір об'єктів, якими нишпорять кілька «маленьких роботів» – потоків – і виконують команди, які є у методах.

Формально – друге не скасовує перше. Це все ще об'єкти, і вони все ще викликають методи один в одного. Але треба не забувати, що потоків – кілька, і кожен потік виконує свою роботу – своє завдання.

Програма стає складнішою. Різні потоки змінюють стан різних об'єктів відповідно до завдання, яке кожен з них виконує. І можуть заважати один одному.

Але найстрашніше відбувається глибоко всередині Java-машини. Як я вже розповідала, видима одночасність роботи потоків досягається завдяки тому, що процесор постійно перемикається з одного потока на інший. Переключився на потік, попрацював 10 мілісекунд, переключився на наступний потік, там попрацював 10 мілісекунд тощо. І тут виникає проблема: перемикання може відбутися в невдалий момент. Приклад:

Код першого потоку Код другого потоку
System.out.print ("Миколі"); System.out.print (" "); System.out.print ("15"); System.out.print (" "); System.out.print ("років");
System.out.println ();
System.out.print ("Олені"); System.out.print (" "); System.out.print ("21"); System.out.print (" "); System.out.print ("рік");
System.out.println ();
Очікуване виведення в консолі
Миколі 15 років
Олені 21 рік
Підсумковий порядок Код першого потоку Код другого потоку
System.out.print ("Миколі");
System.out.print ("Олені"); System.out.print (" ");
System.out.print (" "); System.out.print ("15");
System.out.print ("21"); System.out.print (" ");
System.out.print (" "); System.out.print ("років"); System.out.println ();
System.out.print ("рік"); System.out.println ();
System.out.print ("Миколі");
//виконується другий потік
//виконується другий потік
System.out.print (" "); System.out.print ("15"); //виконується другий потік
//виконується другий потік
System.out.print (" "); System.out.print ("років"); System.out.println ();
//виконується другий потік
//виконується другий потік
//виконується другий потік
System.out.print ("Олені"); System.out.print (" ");
//виконується другий потік
//виконується другий потік
System.out.print ("21"); System.out.print (" ");
//виконується другий потік
//виконується другий потік
//виконується другий потік
System.out.print ("рік"); System.out.println ();
Реальне виведення в консолі
Миколі 
Олені 
15 
21 
років
рік

Або ось ще приклад:

Код Опис
class MyClass {
    private String name1 = "Оля";
    private String name2 = "Олена";
    public void swap() {
        String s = name1;
        name1 = name2;
        name2 = s;
    }
}
Метод swap міняє місцями значення змінних name1 & name2.

Що ж буде, якщо його викликати з двох потоків одночасно?

Підсумковий порядок Код першого потоку Код другого потоку
String s1 = name1; //Оля
name1 = name2; //Олена
String s2 = name1; //Олена(!)
name1 = name2; //Олена
name2 = s1; //Оля
name2 = s2; //Олена
String s1 = name1; name1 = name2;
//виконується другий потік
//виконується другий потік
name2 = s1;
//виконується другий потік
//виконується другий потік
//виконується другий потік
String s2 = name1; name1 = name2;
//виконується другий потік
name2 = s2;
Підсумок
Обидві змінні мають значення «Олена».
Об'єкт «Оля» було перезатерто і втрачено.

— Хто б міг подумати, що за елементарного присвоєння можливі такі помилки?

— Так, але для цієї проблеми є рішення. Але про це трохи пізніше – у мене горло пересохло.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ