Читайте также:
|
|
Тема: «Потоки исполнения в приложениях Java».
Потоки исполнения (thread) представляют собой составляющую программного исполнения, которая работает независимо от других потоков. Java-программы могут состоять из множества независимых потоков, исполняемых как будто на независимых процессорах, реализуемых на основных пакетах:
· интерфейс java.lang.Runnable определяет метод run(), функционирующий подобно блоку кода, исполняемого потоком;
· можно использовать оператор и модификатор synchronized для написания блоков кода и целых методов, требующих блокирования доступа к ним других потоков. Механизм гарантирует, что два потока не смогут одновременно исполнять блок с общими данными;
· для приостановки и возобновления потоков могут использоваться методы wait() и notify() пакета java.lang.Object.
Пример: Демонстрирует возможности определения и запуска потоков, манипуляции ими, установки приоритетов между ними.
public class ThreadDemo extends Thread {
/**
* Метод замещает метод run() класса Thread. Он предоставляет
* тело для этого процесса.
**/
public void run() { for(int i = 0; i < 5; i++) compute(); }
/**
* Метод main создает и запускает дополнительно к начальному потоку
* еще два потока для вызова метода main().
**/
public static void main(String[] args) {
// Создание первого потока
// его телом является метод run()
ThreadDemo thread1 = new ThreadDemo();
// создание второго потока исполнения объекта Runnable
// конструктором Thread(). Телом потока является метод run()
// объекта Runnable.
Thread thread2 = new Thread(new Runnable() {
public void run() { for(int i = 0; i < 5; i++) compute(); }
});
// Установка приоритетов потоков
if (args.length >= 1) thread1.setPriority(Integer.parseInt(args[0]));
if (args.length >= 2) thread2.setPriority(Integer.parseInt(args[1]));
// Запуск двух потоков на исполнение
thread1.start();
thread2.start();
// Метод main() запущен начальным потоком исполнения
for(int i = 0; i < 5; i++) compute();
// Завершение потоков
// try {
// thread1.join();
// thread2.join();
// } catch (InterruptedException e) {}
// Java VM завершит работу после метода main()
}
// Объекты ThreadLocal представляют значение, доступ к которым
// осуществляют методы get() и set().
// Объект отслеживает число вызовов метода compute() каждым потоком.
static ThreadLocal numcalls = new ThreadLocal();
/** Подсчет количества вызовов потоком */
static synchronized void compute() {
// отобразить имя потока и количество вызовов
Integer n = (Integer) numcalls.get();
if (n == null) n = new Integer(1);
else n = new Integer(n.intValue() + 1);
numcalls.set(n);
// имя потока и количество вызовов
System.out.println(Thread.currentThread().getName() + ": " + n);
// имитация нагрузки на процессор потока исполнения
for(int i = 0, j=0; i < 1000000; i++) j += i;
// сортировка задержек ввода/вывода
try {
// приостановка исполнения
Thread.sleep((int)(Math.random()*100+1));
}
catch (InterruptedException e) {}
// Потоки исполняются без конфликтов между собой.
Thread.yield();
}
}
Каждый поток исполнения в Java принадлежит какой-либо группе потоков исполнения ThreadGroup, методами которой можно осуществлять управление поведением потоков. Т.е. образуется иерархия групп потоков исполнения.
Пример: Использует класс ThreadLister, содержащий метод listAllThreads(), который отображает эту иерархию, выводя список исполняемых потоков, их приоритетов и групп.
import java.io.*;
import java.awt.*; // AWT классы для демонстрации программы
import javax.swing.*; // Swing GUI для создания интерфейса программы
/**
* Класс содержит метод для вывода потоков исполнения
**/
public class ThreadLister {
/** Отображение информации о потоке. */
private static void printThreadInfo(PrintWriter out, Thread t,
String indent) {
if (t == null) return;
out.println(indent + "Thread: " + t.getName() +
" Priority: " + t.getPriority() +
(t.isDaemon()?" Daemon":"") +
(t.isAlive()?"":" Not Alive"));
}
/** Отображение информации о группе потоков */
private static void printGroupInfo(PrintWriter out, ThreadGroup g,
String indent) {
if (g == null) return;
int num_threads = g.activeCount();
int num_groups = g.activeGroupCount();
Thread[] threads = new Thread[num_threads];
ThreadGroup[] groups = new ThreadGroup[num_groups];
g.enumerate(threads, false);
g.enumerate(groups, false);
out.println(indent + "группа потоков: " + g.getName() +
" наивысший приоритет: " + g.getMaxPriority() +
(g.isDaemon()?" демон":""));
for(int i = 0; i < num_threads; i++)
printThreadInfo(out, threads[i], indent + " ");
for(int i = 0; i < num_groups; i++)
printGroupInfo(out, groups[i], indent + " ");
}
/** находим корневую группу и рекурсивный вывод содержимого */
public static void listAllThreads(PrintWriter out) {
ThreadGroup current_thread_group;
ThreadGroup root_thread_group;
ThreadGroup parent;
// получение группы текущего потока исполнения
current_thread_group = Thread.currentThread().getThreadGroup();
// поиск корневой группы
root_thread_group = current_thread_group;
parent = root_thread_group.getParent();
while(parent!= null) {
root_thread_group = parent;
parent = parent.getParent();
}
// ее вывод
printGroupInfo(out, root_thread_group, "");
}
/**
* Метод main() создает интерфейс пользователя для вывода информации
**/
public static void main(String[] args) {
// Интерфейс Swing GUI
JFrame frame = new JFrame("ThreadLister Demo");
JTextArea textarea = new JTextArea();
frame.getContentPane().add(new JScrollPane(textarea),
BorderLayout.CENTER);
frame.setSize(500, 400);
frame.setVisible(true);
// Формирование строки threadlisting
StringWriter sout = new StringWriter(); // для приема
PrintWriter out = new PrintWriter(sout);
ThreadLister.listAllThreads(out); // вывод списка потоков
out.close();
String threadListing = sout.toString(); // вывод строки
// отображение в интерфейсе GUI
textarea.setText(threadListing);
}
}
Для гарантированного использования только одного потока исполнения в программном приложении используется синхронизация, связанная с наложением исключительной блокировки (exclusive lock). При «взаимной блокировке» (beadlock), когда несколько потоков ожидают ресурс данных, все потоки останавливаются и программа завершается.
Пример: Создает ситуацию взаимной блокировки, в которой два потока пытаются захватить два ресурса.
public class Deadlock {
public static void main(String[] args) {
// Это два ресурса, за которые потоки будут бороться
final Object resource1 = "resource1";
final Object resource2 = "resource2";
// первый поток пытается захватить resource1, затем resource2
Thread t1 = new Thread() {
public void run() {
// захват resource1
synchronized(resource1) {
System.out.println("поток 1: захват resource 1");
// пауза ввода/вывода для запуска второго потока
try { Thread.sleep(50); }
catch (InterruptedException e) {}
// ожидание захвата resource 2
synchronized(resource2) {
System.out.println("поток 1: захвачен resource 2");
}
}
}
};
// второй поток исполнения. Он захватывает resource2, затем resource1
Thread t2 = new Thread() {
public void run() {
// поток захватывает resource 2
synchronized(resource2) {
System.out.println("поток 2: захвачен resource 2");
// пауза.
try { Thread.sleep(50); }
catch (InterruptedException e) {}
// попытка захватить resource1.
// 1 поток захватил resource1 и держит его,
// пока не захватит resource2. Программа зависает
synchronized(resource1) {
System.out.println("поток 2: захвачен resource 1");
}
}
}
};
// запуск двух потоков. Возникает взаимная блокировки и
// программа зависает.
t1.start();
t2.start();
}
}
Пример: С помощью класса java.util.Timer и java.util.TimerTask можно организовать временную последовательность вызова метода run() и организовать управление потоками.
public abstract class TimerTask implements Runnable {
boolean cancelled = false; // Была ли отмена?
long nextTime = -1; // Момент назначения следующего исполнения?
long period; // интервал исполнения
boolean fixedRate; // запуск исполнения с фиксированной частотой?
protected TimerTask() {}
/**
* Прекращение исполнения задачи (true)
* и false, если потока не было.
**/
public boolean cancel() {
if (cancelled) return false; // уже отменена;
cancelled = true; // отмена
if (nextTime == -1) return false; // не исполнялась;
return true;
}
/**
* момент назначения исполнения методом run()
**/
public long scheduledExecutionTime() { return nextTime; }
/**
* подклассы замещают метод, задавая код исполнения.
* класс Timer вызывает его из внутреннего потока исполнения.
**/
public abstract void run();
// класс Timer передает классу Task сведения о расписании.
void schedule(long nextTime, long period, boolean fixedRate) {
this.nextTime = nextTime;
this.period = period;
this.fixedRate = fixedRate;
}
// метод, вызываемый классом Timer после вызова метода run.
boolean reschedule() {
if (period == 0 || cancelled) return false; // не запускать
if (fixedRate) nextTime += period;
else nextTime = System.currentTimeMillis() + period;
return true;
}
}
Пример: Демонстрируются задачи, за исполнение которых отвечает Timer.
import java.util.Date;
import java.util.TreeSet;
import java.util.Comparator;
public class Timer {
// Задачи, за исполнение которых отвечает Timer.
TreeSet tasks = new TreeSet(new TimerTaskComparator());
// организация потока исполнения
TimerThread timer;
/** конструктор, создающий класс Timer, не использующий поток-демон */
public Timer() { this(false); }
/** главный конструктор */
public Timer(boolean isDaemon) {
timer = new TimerThread(isDaemon); // TimerThread определяется ниже
timer.start(); // запуск потока исполнения
}
/** остановка потока timer и завершение задач */
public void cancel() {
synchronized(tasks) { // один поток в момент времени!
timer.pleaseStop(); // флаг на остановку потока
tasks.clear(); // снятие задач
tasks.notify(); // вывод потока из ожидания wait().
}
}
/** однократное исполнение задачи с задержкой */
public void schedule(TimerTask task, long delay) {
task.schedule(System.currentTimeMillis() + delay, 0, false);
schedule(task);
}
/** включение в расписание задачи на данный момент времени */
public void schedule(TimerTask task, Date time) {
task.schedule(time.getTime(), 0, false);
schedule(task);
}
/** запуск периодического исполнения задачи в заданное время */
public void schedule(TimerTask task, Date firstTime, long period) {
task.schedule(firstTime.getTime(), period, false);
schedule(task);
}
/** старт после задержки */
public void schedule(TimerTask task, long delay, long period) {
task.schedule(System.currentTimeMillis() + delay, period, false);
schedule(task);
}
/**
* организация периодического запуска с фиксированной частотой.
**/
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
task.schedule(System.currentTimeMillis() + delay, period, true);
schedule(task);
}
/** расписание исполнения после заданного времени */
public void scheduleAtFixedRate(TimerTask task, Date firstTime,
long period)
{
task.schedule(firstTime.getTime(), period, true);
schedule(task);
}
// внутренний метод добавляет задачу
void schedule(TimerTask task) {
synchronized(tasks) { // один поток в момент времени!
tasks.add(task); // добавка задачи
tasks.notify(); // возобновление потоков
}
}
/**
* внутренний класс для упорядочения задач.
**/
static class TimerTaskComparator implements Comparator {
public int compare(Object a, Object b) {
TimerTask t1 = (TimerTask) a;
TimerTask t2 = (TimerTask) b;
long diff = t1.nextTime - t2.nextTime;
if (diff < 0) return -1;
else if (diff > 0) return 1;
else return 0;
}
public boolean equals(Object o) { return this == o; }
}
/**
* внутренний класс, определяющий поток исполнения
* scheduled times
**/
class TimerThread extends Thread {
// управление остановкой потока.
volatile boolean stopped = false;
// конструктор
public TimerThread(boolean isDaemon) { setDaemon(isDaemon); }
// остановка потока
public void pleaseStop() { stopped = true; }
// тело потока
public void run() {
TimerTask readyToRun = null; // задача?
// организация цикла до остановки потока.
while(!stopped) {
// существование задачи!
if (readyToRun!= null) {
if (readyToRun.cancelled) { // если отмена.
readyToRun = null;
continue;
}
// запуск задачи.
readyToRun.run();
// изменение расписания задачи
if (readyToRun.reschedule())
schedule(readyToRun);
// запуск
readyToRun = null;
// возврат к началу цикла
continue;
}
// блокировка на набор задач
synchronized(tasks) {
long timeout; // время задержки?
if (tasks.isEmpty()) { // ожидание следующей задачи
timeout = 0; // следующая задача
}
else {
// выбор первой задачи
TimerTask t = (TimerTask) tasks.first();
// следующий запуск?
timeout = t.nextTime - System.currentTimeMillis();
// пора запускать
if (timeout <= 0) {
readyToRun = t; // сохранение
tasks.remove(t); // удаление из набора
// выход из синхронизованного раздела
// пока не запущена задача
continue;
}
}
// Ожидание команды notify() для следующего запуска
try { tasks.wait(timeout); }
catch (InterruptedException e) {}
// возвращение к началу цикла while
}
}
}
}
/** внутренний класс тестовой программы */
public static class Test {
public static void main(String[] args) {
final TimerTask t1 = new TimerTask() { // задача 1: печать "boom!"
public void run() { System.out.println("boom!"); }
};
final TimerTask t2 = new TimerTask() { // задача 2: печать "BOOM?"
public void run() { System.out.println("\tBOOM?"); }
};
final TimerTask t3 = new TimerTask() { // задача 3: отмена задач
public void run() { t1.cancel(); t2.cancel(); }
};
// создание объекта timer, и включение нескольких задач
final Timer timer = new Timer();
timer.schedule(t1, 0, 500); // boom каждые 0.5c
timer.schedule(t2, 2000, 2000); // BOOM каждые 2с
timer.schedule(t3, 5000); // остановить через 5c
// последняя задача: начав через 5c, провести обратный отсчет
// от 5, уничтожить timer, завершить программу
timer.scheduleAtFixedRate(new TimerTask() {
public int times = 5;
public void run() {
System.out.println(times--);
if (times == 0) timer.cancel();
}
},
5000,500);
}
}
}
Дата добавления: 2015-09-11; просмотров: 81 | Поможем написать вашу работу | Нарушение авторских прав |