Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

GrandM-Patterns_in_Java

.pdf
Скачиваний:
98
Добавлен:
14.03.2016
Размер:
8.88 Mб
Скачать

DoubIe 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 511

Следующий метод генерирует исключение IOException, если он вызывается после метода close объекта DoubleBufferedInputStream. Он вызывается од­ ним из методов read или s kip класса с тем, чтобы они сгенерировали исклю­

чение IOException в том случае, если эти методы были вызваны после закры­ тия потока. Этот метод проверяет, не равно ли nul l значение переменной buffers. В переменной buffers конструктором класса устанавливается ссыл­ ка на массив массивов; null в ней устанавливается методом c lose.

private void checkClosed ( ) throws IOException

if (buffers == null) {

 

throw new IOException ("Stream closed") ;

}

/ / i f

//

checkClosed ( )

/**

*

*

*

* */

Создает объект DoubleBufferedInputSt ream,

который будет читать входные данные из зтого входного потока, исполь зуя заданный по умолчанию размер буфера и два буфера .

public DoubleBufferedInputStream(InputStream in) (

)

/

 

this (in, DEFAULT_BUFFER_SIZE) ;

 

// con structor ( InputStream)

* *

 

*

 

Создает объект DоublеВuf fеrеdIлрutstrеаm,

*

 

который будет читать

*

 

входные данные из зтого входного потока ,

*

 

используя два буфера заданного размера .

*/

 

public DOubleBufferedInputStream(InputStream in , int size) (

this (in, size , DEFAULT BUFFER COUNT) ;

/ / _ _

сол struсtо r ( IпрutStrеаm, i n t )

/**

*Создает объект Doubl eBufferedInputStream,

*который будет читать входные данные

* * */

из этого входного потока , исполь зуя заданное количество буферов определенных размеров .

512 Глава 9. Шаблоны проектирования для конкурирующих операций

pub1ic DOub1eBufferedInputStream(InputStream in , int size ,

int bufferCount) {

/*

*

*

*

super (in) ;

if

(size < 1) (

 

String тзч = "Buffer size < 1" ;

 

throw new I11egalArgumentException (msg) ;

 

/ / i f s i ze

if

(bufferCount < 2)

 

bufferCount = 2 ;

}

/ / i f

buffers = new byte [bufferCount] [size] ; counts = new int [bufferCount] ; myBufferFi11er = new BufferFi11er ( ) ;

/ / con s t ructor ( I nputStream, int , int )

* Возвращает следующий байт данных или - 1 , если был достигнут конец потока . /

pub1ic synchronized int read () throws IOException {

checkC10sed ( ) ;

if

(eof)

{

}

return

-1 ;

/ / i f

eof

if

(роз

>= counts [activeBuffer] )

 

eof

=

! advanceBuffer ( ) ;

}/ / i f empty

if (eof) { return -1 ;

}

/ / i f

eof

( & )

//

Возвращает результат операции

/ /

над следующим символом и Oxff,

 

/ /

поэтому значения Ox80 - 0xff больше не рассматриваются как

/ /

отрицательные .

 

int с = buffers [activeBuffer] [роз++] , Oxff ;

if

(роз

>= counts [activeBuffer] )

(

 

eof = ! advanceBuffer ( ) ;

 

}

/ / i f

empty

 

return

с ;

 

/ /

read ( )

 

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)

514

Глава 9. Шаблоны проектирования для конкурирующих операций

/ * *

 

 

* Этот метод делает текущий активный буфер пустым

*

резервным буфером, а следующий резервный буфер -

*

новым активным буфером . Если следующий резервный буфер еще

*

не был полностью заполнен и есть основания полагать ,

*

что он будет заполнен,

*

то этот

метод ожидает заполнения .

*

@retu rn

Возвращает true , если этот вызов был успешным

*

*при переходе к другому заполненному буферу .

* / private boolean advanceBuffer ( ) throws IOException (

int nextActiveBuffer = (activeBuffer+l) %counts . length; if (counts [nextActiveBuffer] = =O)

1f (exhausted) ( rethrowException ( ) ; return false ;

/ / i f exhausted

// мы не знаем , существует ли текущий запрос // на заполнение резервного буфера или нет , // поэтому выясняем это .

myBufferFiller . fillReserve () ;

synchronized (lockObject) (

while ( counts [nextActiveBuffer] ==O && ! exhausted) (

try {

10ckObject . wait ( ) ;

) catch (InterruptedExcept10n е) ( IOExcept10n 10е

= new InterruptedIOExcept10n ( ) ; ioe . initCause (e) ;

throw 10е;

//t ry

// whi le

//synchron ized

if (counts [nextActiveBuffer] = =O " exhausted) ( rethrowException ( ) ;

return false ;

//i f exhausted

// if ==0

 

 

 

 

 

DoubIe Buffering 515

/ /

Теперь нам известно ,

что

 

следующий

уфер содержит данные ,

/ /

поэтому делаем его актив

н

ы м .

 

counts [activeBuffer] =

О ;

 

 

 

роз

= О ;

 

 

 

 

activeBuffer = nextActiveBuffer ;

 

//

//

//

Сейчас прежний активуфером, попытаемся понадобится .

ный его

буфер запол

является пу нить до того

стым

,как

р

езе он

р н

внь

ам

/

myBuff8rFil18r . fillRes8rve () ; return tru8 ;

/ / advanceBuffer ( )

* *

в

 

*

текущем потоке повторно сгенерируем исключение, которое

*

мы

перехватили потоком, выполняющим опережающее чтение .

* /

 

 

private void rethrowExc8ption ( )

throws IOException {

if (8xc8ption !=null)

(

 

Throwable е

= exception;

 

8xception

=

null;

 

 

close ( ) ;

 

 

 

 

if

(8 instanceof

IOException)

 

throw

(IOExc8ption) 8 ;

 

 

81se i f ( е instanceof RuntimeException)

 

throw

(RuntimeException)

8 ;

 

81se if

(8

instanceof Error)

 

throw

(Error) е ;

 

 

else (

 

 

 

 

 

IOException ioe ;

 

 

i08 = new IOException ("An error occurred") ;

 

ioe . initCause (e) ;

 

 

throw 10е ;

 

 

 

/ / i f

 

 

 

 

/ /

i f

 

 

 

 

// rethrowException ( )

/* *

*

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

байтов

или

,

*

возможно , меньше . Чтобы избежать проблемы,

связа

нной

с тем,

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 ( )

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]