
- •Пакеты java.lang и java.util.concurrent Потоки и многопоточность
- •Класс java.lang.Thread
- •Поля, конструкторы и методы класса java.lang.Thread (1)
- •Методы класса java.lang.Thread (2)
- •Поля и методы класса java.lang.Thread (3)
- •Поля и методы класса java.lang.Thread (4)
- •Класс java.lang.Thread, интерфейс Runnable
- •Классы java.util.TimerTask и java.util.Timer (1)
- •Классы java.util.TimerTask и java.util.Timer (2)
- •Классы java.util.TimerTask и java.util.Timer (3)
- •Методы классов java.util.TimerTask и java.util.Timer
- •Многопоточность и атомарность операций
- •Гонки данных, пример
- •Хранение данных (1)
- •Хранение данных (2)
- •Хранение данных (3)
- •Взаимодействие потоков, пример:
- •Класс java.lang.ThreadLocal (1)
- •Класс java.lang.ThreadLocal (2) Пример
- •Класс java.lang.ThreadLocal (3)
- •Базовые средства Java для синхронизации потоков (1)
- •Базовые средства Java для синхронизации потоков (2)
- •Базовые средства Java для
- •Базовые средства Java для синхронизации потоков (4)
- •Базовые средства Java для синхронизации потоков (5)

Пакеты java.lang и java.util.concurrent Потоки и многопоточность
Вмомент запуска любого приложения с ним сопоставляется в точности один поток управления. Многие приложения целиком исполняются в этом единственном потоке.
Вплатформе Java с самого начала была заложена возможность образования дополнительных потоков управления, исполняемых параллельно (одновременно или квазиодновременно) в едином адресном пространстве
приложения.
Средства поддержки многопоточности, обеспечивающие все более удобные
и эффективные возможности, добавлялись в JDK 1.2 (класс java.lang.ThreadLocal), в JDK 1.3 (классы java.util.TimerTask и java.util.Timer), в JDK 1.5 (основные классы пакета java.util.concurrent), а затем в JDK 1.6 и 1.7 (классы и интерфейсы пакета java.util.concurrent).
Вначале рассмотрим использование базовых средств платформы Java:
интерфейсы:
java.lang.Runnable
классы
java.lang.Thread
java.lang.ThreadLocal
java.util.TimerTask
java.util.Timer
java.lang.ThreadGroup

Класс java.lang.Thread
Есть три возможности использования класса java.lang.Thread.
1. Можно просто создать свой дочерний класс на базе класса Thread и вызвать его метод start(). При этом фактически возникает новый поток и вызывается метод run().
Вот пример потока, просто выдающего обратный отсчет в секундах: public static void main( String[ ] args ) {
class ThreadTest extends Thread { int count = 4;
public void run( ){ while( --count > 0 ){
System.out.println( "Wait: " + count ); try {
Thread.sleep( 1000 );
} catch ( InterruptedException e ) { e.printStackTrace( ); }
}
System.out.println( "Start!" + " Thread Id: " + Thread.currentThread( ).getId( ) );
} |
|
|
Main thread Id: 1 |
||
} |
||
ThreadTest tt = new ThreadTest( ); |
Wait: 3 |
|
tt.start( ); |
Wait: 2 |
|
System.out.println( "Main thread Id: " + Thread.currentThread( ).getId( ) ); |
Wait: 1 |
|
} |
Start! Thread Id: 8 |

Поля, конструкторы и методы класса java.lang.Thread (1)
public final NORM_PRIORITY |
Нормальный приоритет |
|
static int |
|
|
public final MAX_PRIORITY |
Максимальный приоритет |
|
static int |
|
|
public final MIN_PRIORITY |
Минимальный приоритет |
|
static int |
|
|
public |
Thread( ) |
Конструктор нового объекта Thread |
public |
Thread( Runnable target ) |
Конструктор нового объекта Thread с указанием объекта, |
|
|
для которого будет вызываться метод run |
public |
Thread( Runnable target, String |
Аналогично предыдущему, но дополнительно задается |
|
name ) |
имя нового объекта Thread |
public |
Thread( String name ) |
Конструктор объекта Thread с указанием его имени |
public |
Thread( ThreadGroup group, |
Конструктор нового объекта Thread с указанием группы |
|
Runnable target ) |
потока и объекта, для которого вызывается метод run |
public |
Thread( ThreadGroup group, |
Аналогично предыдущему, но дополнительно задается |
|
Runnable target, String name ) |
имя нового объекта Thread |
public |
Thread( ThreadGroup group, |
Конструктор нового объекта Thread с указанием группы |
|
String name ) |
потока и имени объекта |

Методы класса java.lang.Thread (2)
public static int |
activeCount( ) |
Возвращает текущее количество активных потоков в группе, |
|
|
|
|
к которой принадлежит поток |
public void |
checkAccesss( ) |
Разрешает текущему потоку изменять этот объект Thread |
|
public int |
countStackFrames( ) |
|
Возвращает количество фреймов в стеке (может быть |
|
|||
|
|
|
вызван только для потоков, приостановленных с помощью |
|
|
|
метода suspend( )) |
public static Thread |
currentThread( ) |
Возвращает текущий работающий поток |
|
public void |
destroy( ) |
|
Принудительное завершение работы потока |
public static void |
dumpStack( ) |
Вывод на консоль текущего содержимого стека для отладки |
|
public static int |
enumerate( Thread |
Заполняет указанный массив активными объектами Thread |
|
|
tarray[] ) |
данной группы и всех ее подгрупп; возвращает количество |
|
|
|
|
заполненных элементов массива |
public final String |
getName( ) |
Возвращает имя текущего потока |
|
public final int |
getPriority( ) |
Возвращает текущий приоритет потока |
|
public final |
getThreadGroup( ) |
Возвращает группу, к которой принадлежит поток |
|
ThreadGroup |
|
|
|

Поля и методы класса java.lang.Thread (3)
public void |
interrupt( ) |
Прерывание выполнения метода sleep потока |
|
public static |
interrupted( ) |
Проверка, прервано ли выполнение текущего потока |
|
boolean |
|
|
|
public final |
isAlive( ) |
Проверка, выполняется поток или нет (может спать или ждать |
|
boolean |
|
|
извещения) |
public final |
isDaemon( ) |
Проверка, является ли поток демоном Обычно потоки-демоны |
|
boolean |
|
|
создаются для обслуживания некритичных задач. Виртуальная |
|
|
|
машина завершает свою работу, если завершились все потоки, не |
|
|
|
являющиеся демонами. В этот момент демоны завершаются |
|
|
|
принудительно |
public boolean |
isInterrupted( ) |
Проверка, прервано ли выполнение данного потока |
|
public final void |
join( ) |
Ожидание завершения указанного потока |
|
public final void |
join( long millis ) |
Ожидание завершения указанного потока в течение заданного |
|
|
|
|
времени. Время задается в миллисекундах |
public final void |
join( long millis, |
Ожидание завершения указанного потока в течение заданного |
|
|
int nanos ) |
времени. Время задается в миллисекундах и наносекундах |
|
public final void |
resume( ) |
|
Возобновление выполнения временно приостановленного потока |
|
|||
public void |
run( ) |
Метод вызывается неявно в том случае, если поток был создан как |
|
|
|
|
объект с интерфейсом Runnable |

Поля и методы класса java.lang.Thread (4)
public final void |
setDaemon( boolean on ) |
Установка режима демона для потока |
public final void |
setName( String name ) |
Установка имени потока |
public final void |
setPriority( int |
Установка приоритета потока |
|
newPriority ) |
|
public static void |
sleep( long millis ) |
Задержка выполнения потока на заданное время. Время |
|
|
задается в миллисекундах |
public static void |
sleep( long millis, int |
Задержка потока на заданное время. Время задается в |
|
nanos ) |
миллисекундах и наносекундах |
public void |
start( ) |
Инициализация потока и запуск его на выполнение, |
|
|
вызывается метод run |
public final void |
stop( ) |
Останов выполнения потока |
public final void |
stop( Throwable obj ) |
Аварийный останов выполнения потока с заданным |
|
|
исключением |
public final void |
suspend( ) |
Приостановка выполнения потока |
public String |
toString( ) |
Строка, представляющая объект-поток |
public static void |
yield( ) |
Приостановка выполнения текущего потока для того, |
|
|
чтобы управление было передано другому потоку |

Класс java.lang.Thread, интерфейс Runnable
2. Можно реализовать интерфейс Runnable в своем классе, а затем создать объект класса Thread, передав конструктору этот объект как параметр. Этот способ используется, когда разрабатываемый класс должен быть наследником какого-либо другого класса (множественного наследования в Java нет):
public static void main( String[ ] args ) {
class ThreadTest extends java.lang.Object implements Runnable { int count = 4;
public void run( ){ while( --count > 0 ){
System.out.println( "Wait: " + count ); try {
Thread.sleep( 1000 );
} catch ( InterruptedException e ) { e.printStackTrace( ); }
}
System.out.println("Start!" + " Priority: " + Thread.currentThread( ).getPriority( ) );
} |
|
} |
|
|
|
new Thread( new ThreadTest( ) ).start(); |
Priorities: 1:5:10 |
System.out.println(" Priorities: "+Thread.MIN_PRIORITY + |
Wait: 3 |
":" + Thread.NORM_PRIORITY + |
Wait: 2 |
":"+Thread.MAX_PRIORITY ); |
Wait: 1 |
} |
Start! Priority: 5 |
|
|

Классы java.util.TimerTask и java.util.Timer (1)
3. Часто в программе нужно выполнять какие-то действия (дальше будем их
называть задачами - Task) периодически или согласно расписанию. Можно создать дочерний класс на базе класса java.util.TimerTask, а затем создать объект класса java.util.Timer, установив нужный момент (или моменты) активации
задачи.
Класс TimerTask является абстрактным, и служит основой для всех выполняемых по расписанию задач.
Класс Timer обеспечивает создание и управление потоками, на которых выполняются эти задачи.
Пример: import java.util.*;
public class MyTask extends TimerTask { public void run( ) { System.out.println( "Запуск задачи" );
}
}
В данном случае задача сводится к выведению строки на экран.
Класс TimerTask реализует интерфейс java.lang.Runnable. Написанный метод должен вызываться из класса Timer.

Классы java.util.TimerTask и java.util.Timer (2)
После того, как задача запрограммирована, необходимо задать расписание ее выполнения. Для этого с помощью метода schedule() создается интерфейс класса Timer:
import java.util.*;
…
Timer timer = new Timer( ); TimerTask task = new MyTask( );
// Ждать десять секунд, прежде чем выполнить task( )...
timer.schedule( task, 10000 );
(или так: // Ждать пять секунд перед первым выполнением task, // а затем выполнять каждые 10 секунд
timer.schedule( task, 5000, 10000 );
)
Можно задать расписание четырех типов:
•выполнять задачу в строго отведенное время (используется объект Date);
•после некоторой задержки (в миллисекундах);
•через определенные промежутки времени;
•через определенные промежутки времени после заданного начального момента времени (реализуется с помощью метода scheduleAtFixedRate).

Классы java.util.TimerTask и java.util.Timer (3)
Каждый объект Timer создает в точности один фоновый поток. В этом потоке могут выполняться несколько задач, каждая по своему расписанию. Большинству приложений вполне достаточно одного таймера, но в принципе можно использовать любое их количество.
Остановить таймер и уничтожить его поток можно, вызвав метод cancel() объекта Timer. Нужно иметь в виду, что остановленный таймер уже не может быть перезапущен. При необходимости придется создать новый таймер и заново ввести расписание задач.
Timer использует безопасные потоки, это значит, что не нужно выполнять их синхронизацию при использовании одного и того же таймера из разных потоков.
Остановка задачи (и таймера, если задача является единственной у него) часто осуществляется изнутри метода run запущенной задачи. Вызов метода cancel() из метода run() гарантирует, что текущее выполнение задачи является последним.
Существует возможность указать, что если по каким-то причинам выполнение задачи оказалось отсроченным по сравнению с требуемым моментом (скажем из-за уборки мусора), то два или более последующих ее выполнения будут происходить через меньшие интервалы времени для того, чтобы наверстать упущенное и войти в первоначально намеченный график. Такой режим планирования может оказаться необходимым в некоторых приложениях.