Потоки в J2SE 5

Java всегда предлагала широкие возможности для мультипрограммирования: потоки – это основа языка. Однако очень часто использование таких возможностей становилось ловушкой: правильно написать и отладить многопоточную программу достаточно сложно.

В версии 1.5 языка добавлены пакеты классов java.util.concurrent.locks, java.util.concurrent.atomic, java.util.concurrent, возможности которых обеспечивают более высокую производительность, масштабируемость, построение потокобезопасных блоков параллельных (concurrent) классов, вызов утилит синхронизации, использование семафоров, ключей и atomic-переменных.

Возможности синхронизации существовали и ранее. Практически это означало, что синхронизированные объекты блокировались, хотя необходимо это было далеко не всегда. Например, поток, изменяющий одну часть объекта Hashtable, блокировал работу других потоков, которые хотели прочесть (даже не изменить) совсем другую часть этого объекта. Поэтому введение дополнительных возможностей, связанных с синхронизаций потоков и блокировкой ресурсов довольно логично.

Ограниченно потокобезопасные (thread safe) коллекции и вспомогательные классы управления потоками сосредоточены в пакете java.util.concurrent. Среди них можно отметить:

· параллельные классы очередей ArrayBlockingQueue (FIFO очередь с фиксированой длиной), PriorityBlockingQueue (очередь с приоритетом) и ConcurrentLinkedQueue (FIFO очередь с нефиксированой длиной);

· параллельные аналоги существующих синхронизированых классов-коллекций ConcurrentHashMap (аналог Hashtable) и
CopyOnWriteArrayList (реализация List, оптимизированная для случая, когда количество итераций во много раз превосходит количество вставок и удалений);

· механизм управления заданиями, основанный на возможностях класса Executor, включающий пул потоков и службу их планирования;

· высокопроизводительный класс Lock, поддерживающий ограниченные ожидания снятия блокировки, прерываемые попытки блокировки, очереди блокировки и установку ожидания снятия нескольких блокиро­вок посредством класса Condition;

· классы атомарных переменных (AtomicInteger, AtomicLong,
AtomicReference), а также их высокопроизводительные аналоги SyncronizedInt и др.;

· классы синхронизации общего назначения, такие как Semaphore, CountDownLatch (позволяет потоку ожидать завершения нескольких операций в других потоках), CyclicBarrier (позволяет нескольким потокам ожидать момента, когда они все достигнут какой-либо точки) и Exchanger (позволяет потокам синхронизироваться и обмениваться информацией);

· обработка неотловленных прерываний: класс Thread теперь поддерживает установку обработчика на неотловленные прерывания (подобное ранее было доступно только в ThreadGroup).

Хорошим примером новых возможностей является синхронизация коллекции типа Hashtable. Объект Hashtable синхронизируется целиком, и если один поток занял кэш остальные потоки вынуждены ожидать освобождения объекта. В случае же использования нового класса ConcurrentHashMap практически все операции чтения могут проходить одновременно, операции чтения и записи практически не задерживают друг друга, и только одновременные попытки записи могут блокироваться. В случае же использования данного класса как кэша (спецификой которого является большое количество операций чтения и малое – записи), блокироваться многопоточный код практически не будет.

В таблице приведено время выполнения (в миллисекундах) программы, использовавшей в качестве кэша ConcurrentHashMap и Hashtable. Тесты проводились на двухпроцессорном сервере под управлением Linux. Количество данных для большего количества потоков увеличивалось.

Количество
потоков

ConcurrentHashMap

Hashtable

1

1.00

1.03

2

2.59

32.40

4

5.58

78.23

8

13.21

163.48

16

27.58

341.21

32

57.27

778.41

// пример # 13 : применение семафора: Sort.java : ArraySort.java

package chapt14;

import java.util.concurrent.*;

public class Sort {

public static final int ITEMS_COUNT = 15;

public static double items[];

// семафор, контролирующий разрешение на доступ к элементам массива

public static Semaphore sortSemaphore =

new Semaphore(0, true);

public static void main(String[] args) {

items = new double[ITEMS_COUNT];

for(int i = 0 ; i < items.length ; ++i)

items[i] = Math.random();

new Thread(new ArraySort(items)).start();

for(int i = 0 ; i < items.length ; ++i) {

/*

* при проверке доступности элемента сортируемого

* массива происходит блокировка главного потока

* до освобождения семафора

*/

sortSemaphore.acquireUninterruptibly();

System.out.println(items[i]);

}

}

}

class ArraySort implements Runnable {

private double items[];

public ArraySort(double items[]) {

this.items = items;

}

public void run(){

for(int i = 0 ; i < items.length – 1 ; ++i) {

for(int j = i + 1 ; j < items.length ; ++j) {

if( items[i] < items[j] ) {

double tmp = items[i];

items[i] = items[j];

items[j] = tmp;

}

}

// освобождение семафора

Sort.sortSemaphore.release();

try {

Thread.sleep(71);

} catch (InterruptedException e) {

System.err.print(e);

}

}

Sort.sortSemaphore.release();

}

}

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100