Слайд 2Базові поняття
Потоки (threads) - підтримка багатопоточності на рівні самої мови.
В одній програмі
можна запустити декілька потоків, які будуть працювати паралельно.
Слайд 3Деякі застосування
“Чутливі” графічні застосування: в одному потоці виконується анімація та інші операції,
а в іншому – кнопка для зупинки та інші елементи керування.
Сокети.
Слайд 4Потоки: основи
Кожний потік є екземпляром класу Thread.
Функціональність потоку визначається методом run(). Потік
завершується, коли завершується виконання коду цього методу (або виникає неперехоплене виключення).
Запуск потоку здійснюється методом start().
Слайд 5Організація потоків: два способи
створення екземпляру класу, похідного від класу Thread;
реалізація інтерфейсу Runnable;
досить типовим є використання анонімних класів.
Слайд 6Приклад запуску потоку
Thread t = new Thread (new Runnable(){
public void run()
{
for (int i=0;i<1000;i++) {
result+=Math.random()-0.5;
}
}
});
t.start(); //Запуск потоку
…
t.join(); //Без цього виведення може бути некоректним
System.out.println(“Result is "+result);
Слайд 7Більш простий запуск (якщо достатньо анонімного потоку)
new Thread () {
@Override
public void run() {
for (int i=0;i<1000;i++) {
result+=Math.random()-0.5;
}
System.out.println("Test indicator is "+result);
}
}.start();
Слайд 8Клас, похідний від Thread
Основна функціональність потоку визначається в методі run().
Щоб запустити потік,
потрібно викликати метод start(), який викликає run().
Самого лише створення екземпляру недостатньо для запуску потоку.
Якщо просто викликати run() - потік не запускається.
Часто start() викликається в конструкторі, і тоді потік запускається при створенні екземпляра.
Слайд 9Реалізація інтерфейсу Runnable
Наприклад, якщо клас уже є підкласом деякого іншого класу.
Функціональність визначається
методом run().
Типова схема запуску потоку: створюється екземпляр класу Thread, конструктору якого передається вказівник на екземпляр нашого класу. Далі викликається start().
Знову-ж таки - часто в конструкторі, і тоді:
Thread tr = new Thread(this);
tr.start();
Досить типово – як анонімний клас.
Слайд 10Приклад програми з кількома потоками
Програма demopotok в каталозі threads.
Створюються три потоки; вони
реалізують той самий код, але роблять різні затримки, і тому працюють з різними швидкостями.
Кожний потік видає повідомлення про поточні значення змінних.
Кожний потік зупиняється після заданої кількості проходів.
Слайд 11Коментарі: функціональність потоку
public void run() {
while (PassesPasses++;
TotalPasses++;
System.out.println(Passes+"-th pass of thread number
"+Nomer+"; total passes - "+TotalPasses);
try {sleep(Delay);
}
catch (InterruptedException e) {
System.out.println("Something strange...");
}}
System.out.println ("Thread number "+Nomer+" stopped");
}
Слайд 12Коментарі: конструктор
public potok(int Delay) {
this.Delay=Delay;
this.start();
Number++;
Nomer=Number;
System.out.println("Thread number "+Nomer+ " started");
}
Слайд 13Коментарі: початкова ініціалізація
static int MaxPasses;
static {System.out.println("Enter amounts of passes");
try {BufferedReader br=new BufferedReader(
new InputStreamReader
(System.in));
MaxPasses = Integer.parseInt(br.readLine());
}
catch (Exception e) {System.exit(1);}
}
static int Number=0;
static int TotalPasses=0;
int Nomer=0;
int Passes=0;
int Delay;
Слайд 14Коментарі: запуск потоків
potok tr1=new potok(1000);
potok tr2=new potok(2000);
potok tr3=new potok(5000);
Слайд 15Використання змінних
Кожний потік отримує власні копії локальних змінних та полів екземпляра, але
статичні поля спільно використовуються всіма потоками
Слайд 16Пріоритети потоків
Потоки можуть виконуватися з різними пріоритетами.
Константи класу Thread: MIN_PRIORITY=1; NORM_PRIORITY=5; MAX_PRIORITY=10
Пріоритет
може бути встановлений методом setPriority, хоча ручне управління пріоритетами не дуже рекомендується.
Слайд 17Зупинка потоку
Є метод для явної зупинки, але ним користуватися не рекомендується.
Краще -
цикл, в якому задати умову завершення потоку.
Слайд 18Метод interrupt
Запит на переривання потоку.
Встановлює в потоці статус переривання.
Не є примусовим перериванням!
Потік має сам вирішити, як обробляти цей запит!
Слайд 19Приклад застосування interrupt
Thread t = new MyThread();
t.start();
t.interrupt();
try {
t.join();
}
catch (InterruptedException ir) {
System.out.println("InterruptedException happened");
}
}
Слайд 201-й варіант потоку: interrupt не працює
class MyThread extends Thread {
@Override
public
void run() {
boolean b=true;
while (b) {
if (isInterrupted()) System.out.println("Request for interruption received");
System.out.println("I am still working!");
}
System.out.println("Thread finished");
}
}
Слайд 212-й варіант: потік зупиняється
public void run() {
boolean b=true;
while (b) {
if (isInterrupted()) {
System.out.println("Request for interruption received");
b=false;
}
System.out.println("I am still working!");
}
System.out.println("Thread finished");
}
Слайд 22Затримка потоку
Метод sleep().
Або викликається для конкретного потоку, або Thread.sleep(мс);
Слайд 23Метод join
Дозволяє дочекатися завершення іншого потоку. Наприклад, якщо потік tr1 робить виклик
tr2.join(), то він призупиняється і чекає, поки tr2 не завершить роботу.
Слайд 24Стани потоків
New – створений, але не запущений.
Runnable – запущений методом start.
Dead –
зупинений.
Block – заблокований. Переходить до заблокованого стану після методу sleep або wait, або якщо він чекає завершення операції введення-виведення, або якщо він чекає завершення синхронізованого методу.
Слайд 25Метод yield
Передає управління іншому потоку з тим же пріоритетом.
Слайд 26Синхронізація потоків
Проблема полягає в тому, що часто потрібно заборонити одночасний доступ різних
потоків до певних об’єктів або одночасний виклик певних методів.
Основні методи синхронізації – за ресурсами та за подіями.
Слайд 27Синхронізація за ресурсами
Ключове слово synchronized - у формі оператора та у формі
модифікатора.
Слайд 28Оператор synchronized
synchronized (expression) {
statements
}
Вираз expression повертає об’єкт або масив. Перед виконанням критичної
секції (statements) цей об’єкт блокується, і ніякий інший потік не може мати до нього доступу, поки виконується критична секція.
Слайд 29Приклад: сортування масиву
public static void sortIntArray( int[] a) {
…
synchronized (a) {
Sorting
}
}
Слайд 30Synchronized як модифікатор
Якщо модифікатор synchronized використовується в описі методу - весь метод
оголошується критичною секцією.
Слайд 31Синхронізовані колекції
Collections.synchronizedList(new ArrayList());
Concurrent API.