Философия Java3 - Брюс Эккель
Шрифт:
Интервал:
Закладка:
Когда управление переходит в цикл while, вызывается метод mark() для установки значения метки (шаг). Состояние буфера в этот момент таково:
QJoS]
| пгаг I
I cap 1
и
S
i
п
g
в
и
f
f
е
г
s
lim
Два вызова «относительных» методов get() сохраняют значение первых двух символов в переменных cl и с2. После этих вызовов буфер выглядит так:
mar
ГроЛ
Два вызова метода put() записывают с2, а затем cl:
I Pos I
cap]
>
г
1
г
и
S
i
п
g
в
U
f
f
е
г
S
к
к
k
к
lim
pos |
Для смешивания символов нам нужно записать символ с2 в позицию 0, a cl в позицию 1. Для этого можно обратиться за .«абсолютной» версией метода put(), но мы приравняем позицию метке, что и делает метод resetQ:
| mar
1
г
1
г
и
S
i
п
g
в
U
f
f
е
г
S
к
к
)
к
□з
cap |
>
г
1
г
и
S
i
п
g
в
U
f
f
е
г
S
i
к
k
к
Rim
На следующей итерации значение метки приравнивается позиции:
mar
cap |
>
г
>
г
и
S
i
п
g
в
U
f
f
е
г
s
k
к
k
к
lim
pos I
Процесс продолжается до тех пор, пока не будет просмотрен весь буфер. В конце цикла while позиция находится в конце буфера. При выводе буфера на печать распечатываются только символы, находящиеся между позицией и предельным значением. Поэтому, если вы хотите распечатать буфер целиком, придется установить позицию на начало буфера, используя для этого метод rewind(). Вот в каком состоянии находится буфер после вызова метода rewind() (значение метки стало неопределенным):
г
и
S
i
п
g
в
U
f
f
е
г
S
i
к
k
к
posl
lim
При следующем вызове symmetricScramble() процесс повторяется, и буфер CharBuffer возвращается к своему изначальному состоянию.
Отображаемые в память файлы
Механизм отображения файлов в память позволяет создавать и изменять файлы, размер которых слишком велик для прямого размещения в памяти. В таком случае вы считаете, что файл целиком находится в памяти, и работаете с ним как с очень большим массивом. Такой подход значительно упрощает код изменения файла. Небольшой пример:
//. io/LargeMappedFiles.java
// Создание очень большого файла, отображаемого в память
// {RunByHand}
import java.nio.*;
import java nio.channels.*;
import java.io.*;
import static net.mindview.util.Print.*.
public class LargeMappedFiles {
static int length = 0x8FFFFFF, // 128 MB public static void main(String[] args) throws Exception { MappedByteBuffer out =
new RandomAccessFileC'test.dat". "rw").getChannel() .map(Fi1eChannel.MapMode.READ_WRITE. 0. length); for(int i = 0; i < length; i++)
out.put((byte)'x'); print("Запись завершена"); for(int i = length/2; i < length/2 +6; i++) printnb((char)out.get(i));
}
} ///:-
Чтобы одновременно выполнять чтение и запись, мы начинаем с создания объекта RandomAccessFile, получаем для этого файла канал, а затем вызываем метод тар(), чтобы получить буфер MappedByteBuffer, который представляет собой разновидность буфера прямого доступа. Заметьте, что необходимо указать начальную точку и длину участка, который будет проецироваться, то есть у вас есть возможность отображать маленькие участки больших файлов.
Класс MappedByteBuffer унаследован от буфера ByteBuffer, поэтому он содержит все методы последнего. Здесь представлены только простейшие вызовы методов put() и get(), но вы также можете использовать такие возможности, как метод asCharBuffer() и т. п.
Программа напрямую создает файл размером 128 Мбайт; скорее всего, это превышает ограничения вашей операционной системы на размер блока данных, находящегося в памяти. Однако создается впечатление, что весь файл доступен сразу, поскольку только часть его подгружается в память, в то время как остальные части выгружены. Таким образом можно работать с очень большими (размером до 2 Гбайт) файлами. Заметьте, что для достижения максимальной производительности используются низкоуровневые механизмы отображения файлов используемой операционной системы.
Производительность
Хотя быстродействие «старого» ввода/вывода было улучшено за счет переписывания его с учетом новых библиотек nio, техника отображения файлов качественно эффективнее. Следующая программа выполняет простое сравнение производительности:
//. io/MappedIO.java import java.nio.*. import java.nio.channels *; import java io.*,
public class MappedIO {
private static int numOflnts = 4000000, private static int numOfUbufflnts = 200000; private abstract static class Tester { private String name,
public Tester(String name) { this name = name. } public void runTestO {
System.out.print(name + ": "). try {
long start = System nanoTimeO; test();
double duration = System.nanoTimeO - start; System.out.formates 2fn", duration/1.0e9); } catch(IOException e) {
throw new RuntimeException(e).
}
}
public abstract void testO throws IOException;
}
private static Tester[] tests = {
new Tester("Stream Write") {
public void testO throws IOException {
DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(
new FileOutputStream(new
FileC temp, tmp"))));
for(int i = 0; i < numOflnts; i++) dos writelnt(i); dos.closeO;
}
}.
new Tester("Mapped Write") {
public void testO throws IOException { FileChannel fc =
new RandomAccessFile("temp.tmp". "rw") getChannel(); IntBuffer ib = fc map(
FileChannel .MapMode READ_WRITE, 0. fc sizeO) .asIntBufferO; for(int i = 0; i < numOflnts; i++)
ib.putCi); fc.closeO;
}
}.
new Tester("Stream Read") {
public void testO throws IOException {
DatalnputStream dis = new DatalnputStreamC new BufferedlnputStreamC
new F i1eInputSt rearn("temp.tmp"))); for(int i = 0; i < numOflnts; i++)
dis.readlntO; dis.closeO;
}
}.
new Tester("Mapped Read") {
public void testO throws IOException {
FileChannel fc = new FileInputStream(
new File("temp.tmp")).getChannelО; IntBuffer ib = fc.map(
Fi 1 eChannel.MapMode.READ_0NLY, 0. fc.sizeO) .asIntBufferO; while(ib. hasRemainingO)
ib.getO; fc. closeO;
}
}.
new Tester("Stream Read/Write") {
public void testO throws IOException {
RandomAccessFile raf = new RandomAccessFile(
new FileC'temp.tmp"), "rw"); raf.writelnt(l);
for(int i = 0; i < numOfUbufflnts; i++) { raf.seek(raf.length() - 4); raf,writelnt(raf.readlnt());
}
raf.closeO;
}
}.
new Tester("Mapped Read/Write") {
public void testO throws IOException {
FileChannel fc = new RandomAccessFile(
new FileC'temp.tmp"). "rw").getChannelО; IntBuffer ib e fc.map(
FileChannel.MapMode.READ_WRITE. 0. fc.sizeO) .asIntBufferO; ib.put(O);
for(int i = 1; i < numOfUbufflnts; i++)
ib.put(ib.get(i - 1)); fc.closeO;
}
}
}:
public static void main(String[] args) { for(Tester test ; tests) test.runTestO;
}
} /* Output; Stream Write; 0.56 Mapped Write; 0.12 Stream Read; 0.80
Mapped Read: 0.07 продолжение &
Stream Read/Write 5.32 Mapped Read/Write 0.02 */// ~
Как уже было видно из предыдущих примеров книги, runTest() — не что иное как метод шаблона, предоставляющий тестовую инфраструктуру для различных реализаций метода test(), определенного в безымянных внутренних подклассах. Каждый из этих подклассов выполняет свой вид теста, таким образом, методы test() также являются прототипами для выполнения различных действий, связанных с вводом/выводом.
Хотя кажется, что для отображаемой записи следует использовать поток FileOutputStream, на самом деле любые операции отображаемого вывода должны проходить через класс RandomAccessFile так же, как выполняется чтение/запись в рассмотренном примере.
Отметьте, что в методах test() также учитывается инициализация различных объектов для работы с вводом/выводом, и, несмотря на то что настройка отображаемых файлов может быть затратной, общее преимущество по сравнению с потоковым вводом/выводом все равно получается весьма значительным.
Блокировка файлов
Блокировка файлов позволяет синхронизировать доступ к файлу как к совместно используемому ресурсу. Впрочем, потоки, претендующие на один и тот же файл, могут принадлежать различным виртуальным машинам JVM, или один поток может быть Java-потоком, а другой представлять собой обычный поток операционной системы. Блокированные файлы видны другим процессам операционной системы, поскольку механизм блокировки Java напрямую связан со средствами операционной системы. Вот простой пример блокировки файла:
//: io/Fi1eLocking.java import java.nio.channels.*; import java.util.concurrent.*; import java.io.*;
public class FileLocking {
public static void main(String[] args) throws Exception {
FileOutputStream fos= new FileOutputStreamCfile txt");
FileLock fl = fos.getChannel О .tryLockO.
if(fl != null) {
System.out.println("Файл заблокирован"); Ti meUnit.MILLISECONDS.sieep(100); fl .releaseO;