|
|||||||
АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Старт обычного потоказавершение обычного потока Поток-демон (из-за вызова метода sleep(10000)) не успел завершить выполнение своего кода до завершения основного потока приложения, связанного с методом main(). Базовое свойство потоков-демонов заключается в возможности основного потока приложения завершить выполнение потока-демона (в отличие от обычных потоков) с окончанием кода метода main(), не обращая внимания на то, что поток-демон еще работает. Если уменьшать время задержки потока-демона, то он может успеть завершить свое выполнение до окончания работы основного потока. Потоки в графических приложениях Добавить анимацию в апплет можно при использовании потоков. Поток, ассоциированный с апплетом, следует запускать тогда, когда апплет становится видимым, и останавливать при сворачивании браузера. В этом случае метод /* пример # 7: освобождение ресурсов апплетом: GraphicThreadsDemo.java */ package chapt14; import java.awt.Color; import java.awt.Container; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel;
public class GraphicThreadsDemo extends JFrame { JPanel panel = new JPanel(); Graphics g; JButton btn = new JButton("Добавить шарик"); int i;
public GraphicThreadsDemo() { setBounds(100, 200, 270, 350); Container contentPane = getContentPane(); contentPane.setLayout(null); btn.setBounds(50, 10, 160, 20); contentPane.add(btn); panel.setBounds(30, 40, 200, 200); panel.setBackground(Color.WHITE); contentPane.add(panel); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { new BallThread(panel).start(); i++; repaint(); } }); } public static void main(String[] args) { GraphicThreadsDemo frame = new GraphicThreadsDemo(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } public void paint(Graphics g){ super. paint(g); g.drawString("Количество шариков: " + i, 65, 300); } } class BallThread extends Thread { JPanel panel; private int posX, posY; private final int BALL_SIZE = 10; private double alpha; private int SPEED = 4;
BallThread(JPanel p) { this. panel = p; //задание начальной позиции и направления шарика posX = (int)((panel.getWidth() - BALL_SIZE) * Math.random()); posY = (int)((panel.getHeight() - BALL_SIZE) * Math.random()); alpha = Math.random() * 10; } public void run() { while (true) { posX += (int)(SPEED * Math.cos(alpha)); posY += (int)(SPEED * Math.sin(alpha)); //вычисление угла отражения if (posX >= panel.getWidth() - BALL_SIZE) alpha = alpha + Math.PI - 2 * alpha; else if (posX <= 0) alpha = Math.PI - alpha; if (posY >= panel.getHeight() - BALL_SIZE) alpha = -alpha; else if (posY <= 0) alpha = -alpha; paint(panel.getGraphics()); } } public void paint(Graphics g) { //прорисовка шарика g.setColor(Color.BLACK); g.fillArc(posX, posY, BALL_SIZE, BALL_SIZE, 0, 360); g.setColor(Color.WHITE); g.drawArc(posX + 1, posY + 1, BALL_SIZE, BALL_SIZE, 120, 30); try { sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } //удаление шарика g.setColor(panel.getBackground()); g.fillArc(posX, posY, BALL_SIZE, BALL_SIZE, 0, 360); } } Рис.14.2. Потоки в апплетах При вызове метода stop() апплета поток перестает существовать, так как ссылка на него устанавливается в null и освобождает ресурсы. Для следующего запуска потока необходимо вновь инициализировать ссылку и вызвать метод start() потока. Методы synchronized Очень часто возникает ситуация, когда несколько потоков, обращающихся к некоторому общему ресурсу, начинают мешать друг другу; более того, они могут повредить этот общий ресурс. Например, когда два потока записывают информацию в файл/объект/поток. Для предотвращения такой ситуации может использоваться ключевое слово synchronized. Синхронизации не требуют только атомарные процессы по записи/чтению, не превышающие по объему 32 бит. В качестве примера будет рассмотрен процесс записи информации в файл двумя конкурирующими потоками. В методе main() классa SynchroThreads создаются два потока. В этом же методе создается экземпляр класса Synchro, содержащий поле типа FileWriter, связанное с файлом на диске. Экземпляр Synchro передается в качестве параметра обоим потокам. Первый поток записывает строку методом writing() в экземпляр класса Synchro. Второй поток также пытается сделать запись строки в тот же самый объект Synchro. Для избежания одновременной записи такие методы объявляются как synchronized. Синхронизированный метод изолирует объект, после чего объект становится недоступным для других потоков. Изоляция снимается, когда поток полностью выполнит соответствующий метод. Другой способ снятия изоляции – вызов метода wait() из изолированного метода. В примере продемонстрирован вариант синхронизации файла для защиты от одновременной записи информации в файл двумя различными потоками. /* пример # 8: синхронизация записи информации в файл: MyThread.java: Synchro.java: SynchroThreads.java */ package chapt14; import java.io.*;
public class Synchro { private FileWriter fileWriter;
public Synchro(String file) throws IOException { fileWriter = new FileWriter(file, true); } public void close() { try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } public synchronized void writing(String str, int i) { try { System. out. print(str + i); fileWriter.append(str + i); Thread. sleep ((long)(Math. random () * 50)); System. out. print("->" + i + " "); fileWriter.append("->" + i + " "); } catch (IOException e) { System. err. print("ошибка файла"); e.printStackTrace(); } catch (InterruptedException e) { System. err. print("ошибка потока"); e.printStackTrace(); } } } package chapt14;
public class MyThread extends Thread { private Synchro s;
public MyThread(String str, Synchro s) { super (str); this. s = s; } public void run() { for (int i = 0; i < 5; i++) { s.writing(getName(), i); } } } package chapt14; import java.io.*;
public class SynchroThreads { public static void main(String[] args) { try { Synchro s = new Synchro("c:\\temp\\data.txt");
MyThread t1 = new MyThread("First", s); MyThread t2 = new MyThread("Second", s); t1.start(); t2.start(); t1.join(); t2.join(); s.close(); } catch (IOException e) { System. err. print("ошибка файла"); e.printStackTrace(); } catch (InterruptedException e) { System. err. print("ошибка потока"); e.printStackTrace(); } } } В результате в файл будет выведено: First0->0 Second0->0 First1->1 Second1->1 First2->2 Second2->2 First3->3 Second3->3 First4->4 Second4->4 Код построен таким образом, что при отключении синхронизации метода writing() при его вызове одним потоком другой поток может вклиниться и произвести запись своей информации, несмотря на то, что метод не завершил запись, инициированную первым потоком. Вывод в этом случае может быть, например, следующим: First0Second0->0 Second1->0 First1->1 First2->1 Second2->2 First3->3 First4->2 Second3->3 Second4->4 ->4 Инструкция synchronized Синхронизировать объект можно не только при помощи методов с соответству- /* пример # 9: блокировка объекта потоком: TwoThread.java */ package chapt14; public class TwoThread { public static void main(String args[]) { final StringBuffer s = new StringBuffer(); new Thread() { public void run() { int i = 0; synchronized (s) { while (i++ < 3) { s.append("A"); try { sleep (100); } catch (InterruptedException e) { System. err. print(e); } System. out. println(s); } } //конец synchronized } }.start(); new Thread() { public void run() { int j = 0; synchronized (s) { while (j++ < 3) { s.append("B"); System. out. println(s); } } //конец synchronized } }.start(); } } В результате компиляции и запуска будет, скорее всего (так как и второй поток может заблокировать объект первым), выведено: A AA AAA AAAB AAABB AAABBB Один из потоков блокирует объект, и до тех пор, пока он не закончит выполнение блока синхронизации, в котором производится изменение значения объекта, ни один другой поток не может вызвать синхронизированный блок для этого объекта. Если в коде убрать синхронизацию объекта s, то вывод будет другим, так как другой поток сможет получить доступ к объекту и изменить его раньше, чем первый закончит выполнение цикла. В следующем примере рассмотрено взаимодействие методов wait() и Метод wait(), вызванный внутри синхронизированного блока или метода, останавливает выполнение текущего потока и освобождает от блокировки захваченный объект, в частности объект lock. Возвратить блокировку объекта потоку можно вызовом метода notify() для конкретного потока или /* пример # 10: взаимодействие wait() и notify(): Blocked.java: Runner.java */ package chapt14;
public class Blocked { private int i = 1000;
public int getI() { return i; } public void setI(int i) { this. i = i; } public synchronized void doWait() { try { System. out. print("Не "); this. wait(); /* остановка потока и освобождение блокировки*/ System. out. print("сущностей "); // после возврата блокировки Thread. sleep (50); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 5; j++) i/=5; System. out. print("сверх "); } } package chapt14;
public class Runner { public static void main(String[] args) { Blocked lock = new Blocked(); new Thread() { public void run() { lock.doWait(); }}.start(); try { Thread. sleep (500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock) { // 1 lock.setI(lock.getI() + 2); System. out. print("преумножай "); lock.notify(); // возврат блокировки } synchronized (lock) { // 2 lock.setI(lock.getI() + 3); //блокировка после doWait() System. out. print("необходимого. "); try { lock.wait(500); } catch (InterruptedException e) { e.printStackTrace(); } } System. out. print("=" + lock.getI()); } } В результате компиляции и запуска будет выведено следующее сообщение: Не преумножай сущностей сверх необходимого. =3 Задержки потоков методом sleep() используются для точной демонстрации последовательности действий, выполняемых потоками. Если же в коде приложения убрать все блоки синхронизации, а также вызовы методов wait() и notify(), то вывод может быть следующим: Не сущностей преумножай необходимого. =1005сверх Состояния потока В классе Thread объявлено внутреннее перечисление State, простейшее применение элементов которого призвано помочь в отслеживании состояний потока в процессе функционирования приложения и, как следствие, в улучшении управления им. /* пример # 11: состояния NEW, RUNNABLE, TIMED_WAITING, TERMINATED: ThreadTimedWaitingStateTest.java */ package chapt14; public class ThreadTimedWaitingStateTest extends Thread { public void run() { try { Thread. sleep (50); } catch (InterruptedException e) { System. err. print("ошибка потока"); } } public static void main(String [] args){ try { Thread thread = new ThreadTimedWaitingStateTest(); // NEW – поток создан, но ещё не запущен System. out. println("1: " + thread.getState()); thread.start(); // RUNNABLE – поток запущен System. out. println("2: " + thread.getState()); Thread.sleep(10); // TIMED_WAITING // поток ждет некоторое время окончания работы другого потока System.out.println("3: " + thread.getState()); thread.join(); // TERMINATED – поток завершил выполнение System.out.println("4: " + thread.getState()); } catch (InterruptedException e) { System.err.print("ошибка потока"); } } } В результате компиляции и запуска будет выведено: NEW Поиск по сайту: |
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.031 сек.) |