c.start ();//Запуск потоку
//Троєкратне зміна дії інкременатора
//з інтервалом в i * 2 секунд (int i=1; i lt;=3; i ++)
{{. sleep (i * 2 * 1000);// Очікування протягом i * 2 сек.
} catch (InterruptedException e) {}
angeAction ();//Перемикання дії
}. finish ();//Ініціація завершення побаченого потоку
}
}
Консоль:
Значення=1 2 1 0 - 1 - 2 - 1 0 1 2 3 4
Взаємодіяти з потоком можна за допомогою методу changeAction () (для зміни віднімання на додавання і навпаки) і методу finish () (для завершення потоку). В оголошенні змінних mIsIncrement і mFinish було використано ключове слово volatile (мінливий, що не постійний). Його необхідно використовувати для змінних, які використовуються різними потоками. Це пов'язано з тим, що значення змінної, оголошеної без volatile, може кешуватися окремо для кожного потоку, і значення з цього кеша може розрізнятися для кожного з них. Оголошення змінної з ключовим словом volatile відключає для неї таке кешування і всі запити до змінної будуть направлятися безпосередньо в пам'ять.
У цьому прикладі показано, яким чином можна організувати взаємодію між потоками. Однак є одна проблема при такому підході до завершення потоку - Incremenator перевіряє значення поля mFinish разів на секунду, тому може пройти до секунди часу між тим, коли буде виконаний метод finish (), і фактичним завершення потоку. Було б чудово, якби при отриманні сигналу ззовні, метод sleep () повертав виконання і потік негайно починав своє завершення. Для виконання такого сценарію існує вбудоване засіб оповіщення потоку, яке називається Interruption (переривання, втручання).
Клас Thread містить в собі приховане булево поле, подібне полю mFinish в програмі Incremenator, яке називається прапором переривання. Встановити цей прапор можна викликавши метод interrupt () потоку. Перевірити ж, чи встановлений цей прапор, можна двома способами. Перший спосіб - викликати метод bool isInterrupted () об'єкта потоку, другий - викликати статичний метод bool Thread.interrupted (). Перший метод повертає стан прапора переривання і залишає цей прапор недоторканим. Другий метод повертає стан прапора і скидає його. Зауважте що Thread.interrupted () - статичний метод класу Thread, і його виклик повертає значення прапора переривання того потоку, з якого він був викликаний. Тому цей метод викликається тільки зсередини потоку і дозволяє потоку перевірити свій стан переривання.
Якщо, повернутися до нашої програми. Механізм переривання дозволить нам вирішити проблему із засипанням потоку. У методів, що припиняють виконання потоку, таких як sleep (), wait () і join () є одна особливість - якщо під час їх виконання буде викликаний метод interrupt () цього потоку, вони, не чекаючи кінця часу очікування, згенерують виняток InterruptedException.
Переробимо програму Incremenator - тепер замість завершення потоку за допомогою методу finish () будемо використовувати стандартний метод interrupt (). А замість перевірки прапора mFinish будемо викликати метод bool Thread.interrupted ();
Так виглядатиме клас Incremenator після додавання підтримки переривань:
Пример 6.
class Incremenator extends Thread
{volatile boolean mIsIncrement=true;
public void changeAction ()//Міняє дію на протилежне
{=! mIsIncrement;
}
Overridevoid run ()
{
{(! Thread.interrupted ())//Перевірка переривання
{(mIsIncrement) Program.mValue ++;//ІнкрементProgram.mValue -;//Декремент
//Висновок поточного значення переменной.out.print (Program.mValue + );
};//Завершення потоку
{. sleep (1000);//Призупинення потоку на 1 сек.
} catch (InterruptedException e) {;//Завершення потоку після переривання
}
} (true);
}
}
Program
{
//змінна, яка оперує інкременатор
public static int mValue=0;
Incremenator mInc;//Об'єкт побаченого потокаstatic void main (String [] args)
{= new Incremenator ();//Створення потока.out.print ( Значення=);
mInc.start ();//Запуск потоку
//Троєкратне зміна дії інкременатора
//з інтервалом в i * 2 секунд (int i=1; i lt;=3; i ++)
{{. sleep (i * 2 * 1000);//Очікування протягом i * 2 сек.
} catch (InterruptedException e) {}
angeAction ();//Перемикання дії
}. interrupt ();//Переривання побаченого потоку
}
}
Консоль:
Значення=1 2 1 0 - 1 - 2 - 1 0 1 2 3 4
Як бачите, ми позбавилися від методу finish () і реалізували той же механізм завершення потоку за допомогою вбудованої системи переривань. У цій реалізації ми отримали одну перевагу - метод sleep () поверне управління (згенерує виняток) негайно після пе...