GrandM-Patterns_in_Java
.pdfDoubIe Buffering - 509
ется только один буфер, выглядит так: за один раз используется строго один бу фер, заполненный данными, и промежуток времени между запросами на получение данных в целом больше, чем промежуток времени, необходимый для заполнения буфера. Библиотека Swing и пользовательские интерфейсы в общем случае могуг служить примерами этого специального случая.
Swing реализует шаблон DoubIe BufТering, используя для этой цели только один буфер, поскольку, даже если производятся быстрые обновления информации, изображаемой на экране, интервал между ними превышает несколько секунд. Это более чем достаточный промежуток времени для того, чтобы нарисовать экранное изображение в буфере перед тем, как оно может понадобиться.
ПРИМЕР КОДА
Примером кодадля этого шаблона может служить подкласс класса j ava . io . In putStream. Он похож на класс j ava . io . BufferedlnputStream за тем ис ключением, что вместо использования одинарной буферизации, позволяющей уменьшить количество операций физического чтения, необходимых ДЛЯ чтения файла, он применяет двойную буферизацию, тоже с целью отказа от необходи мости ожидания операций физического чтения.
public
class DoubleBufferedInputStreaт extends FilterInputStreaт private static final int DEFAULT BUFFER COUNT = 2 ;
private static final int DEFAULT_BUFFER_SIZE = 4096 ;
_ _
private byte [ ] [ ] buffers ;
Двойная буферизация реализуется при помощи массива массивов, на который ссылается переменная buffers. Активный буфер представлен тем массивом, индекс которого в массиве высшего уровня совпадает со значением перемен ной activeBuffer. Другие массивы используются в качестве резервных.
private int activeBuffer = О ;
/ |
* |
* |
|
Количество байтов в каждом буфере . |
* /
private int [ ] counts ;
При заполнении буферов этот класс пытается заполнить их полностью. Но это не всегда возможно. Реальное количество байтов данных, содержащихся в бу фере, находится в элементе массива counts, соответствующем этому буферу.
/ * * * Индекс следующего символа ,
* который должен быть считан из активного буфера .
510 • Глава 9. Шаблоны проектирования дnя конкурирующих операций
*
* Справедливо следующее утверждение :
* |
О <= pos <= counts [act iveBuffe r ] |
* |
|
* |
Если pos==counts [ activeBuffer ] , |
*то активный буфер пуст .
*/
private int роз ;
/ * * |
|
|
* |
Если исключение генерируется в о время |
опережающего чтения |
* |
с целью заполнения резервного буфера , |
этой переменной |
* |
присваивается объект исключения, чтобы он мог быть |
|
* |
повторно сгенерирован позже в потоке, |
запрашивающем данные , |
*когда этот поток достигнет точки , где было сгенерировано исключение .
* /
private Throwable exceptioni
/ * *
*Если эта переменная равна true,
*достигнут конец входного потока .
* /
private boolean exhaustedi
/ * *
* |
Эта переменная становится равной true после того , как весь |
* |
основной входной поток был считан в буфер . |
* / |
|
private boolean eofi |
|
/ * * |
|
* |
Этот объект отвечает за асинхронное |
* заполнение резервных буферов . |
|
* / |
|
private BufferFiller inyBufferFiller i |
Класс Bu f ferFi l ler представляет собой закрытый класс, код которого приво
J.Ится В конце данного листинга.
/ * *
* |
Объект блокировки , который исполь зуется для синхронизации |
||
* |
потоков , запрашивающих данные , и заполнения буферов . |
||
* / |
|
= |
new Object ( ) ; |
private Object lockObject |
|||
|
|
|
DoubIe Buffering 8 f
/ * *
* |
Читает заданное количество байтов и з этого входного |
поток |
* |
в определенный байтовый массив , начиная с заданного |
|
смещения . |
- 1 , |
|
* |
|
|
* |
@ return Возвращает количество считанных байтов или |
*если был достигнут конец потока .
* / |
|
|
|
|
|
|
public synchronized int read (byte Ь [ ] , int off , |
int len) |
|||||
|
|
|
|
|
throws IOException |
|
checkClosed ( ) : |
|
(off + len) I (b . lenqth - (off + len» ) < О |
||||
if « off I len I |
|
|||||
throw new IndexOutOfВoundsException () : |
|
|||||
else if |
(eof) |
|
|
|
|
|
return |
-1: |
-- О ) |
|
|
||
else if |
(len |
{ |
|
|||
return |
О : |
|
|
|
||
/ / i f |
|
|
|
|
|
|
int howМany = О: |
|
|
|
|||
do { |
|
|
|
|
|
|
if (len <= соuпts [асtivеВuffеr] -роs) |
|
|||||
System . arraycopy (buffers [activeBuffer] , |
роа , |
|||||
howМany += |
|
Ь , off , len) : |
|
|||
len ; |
|
|
||||
роа |
+= |
len : |
|
|
|
|
len |
= О : |
|
|
|
|
|
else |
{ |
|
|
|
|
|
int |
remaininq = соuпts [асtivеВuffеr] -роs : |
|||||
System. arraycopy (buffers [activeBuffer] , |
роа , |
|||||
howМany += |
|
Ь , off, remaininq) : |
|
|||
rema1ninq: |
|
|||||
роа |
+= |
remain1nq: |
|
|||
len |
+= |
remaininq: |
|
|||
off |
remaininq: |
|
/ / i f len
if (роа >= counts [act1veBuffer] ) eof = ! advanceBuffer ( ) :
)/ / i f empty
while ( ! eof |
&& len>O ) : |
return howмany: |
|
/ / read (byte [ ] , |
int, int) |
516 • Глава 9. Шаблоны проектирования дпя конкурирующих операций
* |
что этот метод должен |
ожидать завершения |
операции |
||||
* опережающего чтения , |
|
|
|||||
* |
такая реализация метода не пропустит байтов больше, |
||||||
* |
чем их содержится в данный момент в активном и резервном |
||||||
* |
буферах . |
|
|
|
|
|
|
* |
@ return Возвращает реаль ное количество пропущенных байтов . |
||||||
* / |
|
|
|
|
|
|
|
public synchronized lonq skip (lonq n) throws |
IOException { |
||||||
|
checkClosed ( ) i |
|
|
|
|||
|
if (n <= |
О) |
|
{ |
|
|
|
|
return |
О ; |
|
|
|
|
|
|
lonq skipped = О ; |
|
|
|
|||
|
int thisBuffer = activeBuffer i |
|
|||||
|
do { |
|
|
|
|
|
|
|
int remaininq = counts [thisBuffer] -роs i |
|
|||||
|
if (remaininq >= |
n) |
{ |
|
|||
|
роз |
+= ni |
|
|
|
||
|
skipped +== |
ni |
|
|
|||
|
break i |
|
|
|
|||
|
/ / i f |
|
|
|
|
||
|
роз == |
О ; |
|
|
|
||
|
n -= remaininqi |
|
|
||||
|
skipped += remaininqi |
|
|||||
|
thisBuffer = |
(thisBuffer+l) %counts . lenqth i |
while (thisBuffer ! =activeBuffer) i
activeBuffer = thisBufferi myBufferFiller . fillReserve () i return skippedi
// skip ( long)
/* *
* |
Возвращает |
количество байтов, которые могут быть прочитаны |
||
* |
из данного |
входного потока |
без |
блокировки . |
* |
|
|
|
|
* |
Эта реализация возвращает |
общее |
количество байтов данных |
*в активном и резервном буферах .
*/
public synchronized int available ( ) throws IOException {
DoubIe Buffering 8 517
checkClosed () ;
int count = О ;
for (int i=O ; i<counts . lenqth; i++) { count += counts [i] ;
//for
return count;
/ / ava i lable ( )
/**ЗаК1=ывает этот ВХОДНОЙ поток и освобождает
*
* все системные ресурсы, связанные с этим потоком .
*/
public void close ( ) throws IOException { if (buffers == null)
return ; myBufferFiller . close () ; buffers = null ;
counts = null ;
// close
/**
*Этот внутренний класс отвечает
*/за асинхронное заполнение резервных буферов .
private class BufferFiller implements Runnable {
/** |
|
* |
Эта переменная равна true после завершения обращения |
* |
к f i llReserve и остается true ДО тех пор , |
* |
пока не закончится запрошенное асинхронное |
* заполнение резервных буферов . |
|
* / |
|
private boolean outstandinqFillRequest;
/**
*Этот поток отвечает
*за асинхронное заполнение резервных буферов .
*/ private Thread myThread;
BufferFiller ( ) {
myThread = new Thread (this , "Buffer Filler" ) ;
518 • Глава 9. Шаблоны проектирования для конкурирующих операций
|
myThread. start ( ) ; |
|
|
1 1 constructor ( ) |
|
1 * * |
|
|
* |
Синхронное заполнение одного буфера . |
|
* |
Полагаем, что все вопросы синхронизации были |
|
* |
решены тем, кто вызывает зтот метод . |
|
* |
@pa ram |
ndx |
|
Индекс заполняемого буфера . |
|
* |
@ return |
Возврашает количество байтов , считанных в буфер . |
* 1 |
|
|
private |
int fi110neBuffer (int ndx) throws 10Exception ( |
|
|
counts [ndx] = in . read (buffers [ndx] ) ; |
return counts [ndx] ;
1 1 f i llOneBuf fer ( int)
1 * *
*Заполняем любые пустые резервные буферы .
*1
private void fi11 () throws 10Exception (
for ( |
int i = (activeBuffer+1) %counts . 1ength; |
||
|
i |
! = activeBuffer |
|
|
|
&& !myТhread. islnterrupted ( ) ; |
|
|
i = |
(i+1) %counts . 1ength) ( |
|
if |
(counts [i]==O) |
( |
|
|
int |
thisCount = |
fi110neBuffer (i) ; |
|
if |
(thisCount == -1) ( |
1 1 конец файла
exhausted = true;
Thread . currentТhread ( ) . interrupt ( ) j
е1зе {
1 1 Оповещает любой поток,
11 ожидающий самого последнего заполненного буфера .
synchronized (lockObject) (
lockObject . notifyAl1( )
) |
1 1 |
synchroni zed |
11 if |
==0 |
|
11 |
if |
ео! |
1 1 for
1 1 f i l l ( )