Назад
Вперед
6.9. Контроль за выводом в файл
В примере создаются два потока, один из которых
записывает в файл некоторые данные, а другой
контролирует этот процесс. Пример демонстрирует
синхронизацию потоков.
Исходный текст примера
Архив проекта для Java WorkShop 2.0
Немного теории
В предыдущем примере мы использовали
простейшее средство синхронизации потоков -
метод join. Напомним, что этот метод позволяет
одному потоку дождаться завершения другого.
Тем не менее, в ряде случаев требуются более
мощные средства синхронизации. В качестве такого
средства вы можете использовать методы wait и notify.
Синхронизованный метод run потока может перейти
в режим ожидания извещения о возникновении
события, связанного с другим потоком, вызвав
метод wait:
public synchronized void run()
{
. . .
try
{
this.wait();
}
catch(InterruptedException ie)
{
System.out.println(ie.toString());
}
. . .
}
Так как при работе этого метода возможно
возникновение исключения InterruptedException, необходимо
предусмотреть блок try-catch.
Для извещения ожидающего события поток должен
вызвать метод notify, как это показано ниже:
. . .
synchronized(ioc)
{
ioc.notify();
}
. . .
Такая операция должна выполняться
синхронизированным методом либо внутри
синхронизированного блока.
Описание примера
Главный поток нашего автономного приложения Java
запрашивает у пользователя количество блоков,
которые необходимо записать в файл. После этого
запускается поток записи в файл, а он, в свою
очередь, запускает поток, контролирующий этот
процесс. Контролирующий поток сигнализирует о
записи каждого сотого блока, а также показывает
общее количество записанных блоков (рис. 1).
Рис. 1. Контроль за процессом записи в файл
Рассмотрим исходный текст примера.
Главный класс приложения FileSynchro
Главный класс определен следующим образом:
import java.io.*;
import java.util.*;
public class FileSynchro
{
. . .
}
В нем предусмотрено три поля:
int nBlocks = 0;
int i;
fileWriter ioThread;
Первое из них хранит количество записываемых
блоков, второе - номер записываемого блока, а
третье - ссылку на поток, выполняющий запись в
файл.
Метод main класса FileSynchro
Этот метод получает управление сразу после
запуска приложения.
Прежде всего он создает объект класса FileSynchro:
FileSynchro fs = new FileSynchro();
Далее у пользователя запрашивается количество
записываемых блоков:
System.out.println(
"*Multithread file writer*\r\n");
System.out.print(
"Enter number of blocks: ");
fs.nBlocks = getKbdInt();
Записав полученное значение в поле fs.nBlocks, метод
main создает объект класса fileWriter, созданного на
базе класса Thread, и запускает его на выполнение:
fs.ioThread = new fileWriter(fs.nBlocks);
fs.ioThread.start();
Далее метод main дожидается завершения работы
только что запущенного потока:
try
{
fs.ioThread.join();
}
catch(InterruptedException ie)
{
System.out.println(ie.toString());
}
System.out.print(
"All done!\r\nPress <Enter>...");
getKbdByte();
Здесь пока все делается таким же образом, что и
в предыдущем примере. Посмотрим, однако, на
исходный текст класса fileWriter.
Класс fileWriter
Как мы уже говорили, этот класс создан на базе
класса Thread:
class fileWriter extends Thread
{
. . .
}
В нем мы определили всего одно поле nBlocks:
int nBlocks;
Конструктор класса fileWriter записывает сюда
количество блоков, которые необходимо сохранить
в файле:
public fileWriter(int n)
{
nBlocks = n;
}
Метод run класса fileWriter
Когда метод main главного класса приложения
запускает объект fileWriter на выполнение, метод run,
определенный в классе fileWriter, начинает работать в
отдельном потоке.
Прежде всего он создает объект класса ioControl,
который унаследован нами от класса Thread, и
запускает его на выполнение:
int i;
DataOutputStream os;
ioControl ioc = new ioControl(this);
ioc.start();
Объект ioControl следит за процессом записи в файл и
выводит контрольные сообщения на консоль.
Обратите внимание, что мы передаем
конструктору ссылку на объект класса fileWriter. Эта
ссылка нужна объекту ioControl чтобы "знать",
какой объект контролировать.
Как осуществляется этот контроль?
После создания потока вывода, связанного с
файлом, метод run класса fileWriter при записи каждого
сотого блока посылает в объект ioc класса ioControl
извещение, вызывая для этого метод notify:
try
{
os = new DataOutputStream(
new FileOutputStream("testfile.txt"));
for(i = 0; i < nBlocks; i++)
{
os.writeBytes("Test string\r\n");
if(i%100 == 0)
{
synchronized(ioc)
{
ioc.setPosition(i);
ioc.notify();
}
}
}
. . .
Перед извещением метод run передает объекту ioc
номер текущего записанного блока, вызывая для
этого метод setPosition, определенный в классе ioControl.
После завершения цикла записи в файл мы
передаем объекту ioc номер последнего записанного
блока, посылаем извещение, а затем завершаем
поток:
synchronized(ioc)
{
ioc.setPosition(i);
ioc.notify();
ioc.end();
ioc.join();
}
После завершения мы ждем, пока поток
действительно перестанет работать, вызывая
метод join.
Класс ioControl
Этот класс создан на базе класса Thread:
class ioControl extends Thread
{
. . .
}
Поле nPosition хранит последний номер записанной
строки, полученный от объекта класса fileWriter:
int nPosition;
В поле ioThread находится ссылка на поток,
выполняющий запись в файл:
Thread ioThread;
И, наконец, в поле end хранится флаг завершения
работы контролирующего потока:
static boolean end = false;
Конструктор класса ioControl
В качестве единственного параметра
конструктору класса ioControl передается ссылка на
объект, выполняющий запись в файл. Эта ссылка
сохраняется в поле ioThread:
public ioControl(Thread th)
{
ioThread = th;
}
Метод setPosition
Этот метод просто сохраняет в поле nPosition номер
блока, записанного в файл:
public void setPosition(int i)
{
nPosition = i;
}
Метод end
Когда поток, выполняющий запись в файл, хочет
завершить контролирующий поток, он вызывает
метод end:
public void end()
{
end = true;
}
Данный метод устанавливает флаг end, проверяемый
методом run.
Метод run
Метод run запускает цикл, условием завершения
которого является установка флага end:
public synchronized void run()
{
while(!end)
{
. . .
}
}
Внутри цикла выполняется ожидание извещения от
потока, занятого записью данных в файл:
try
{
this.wait();
}
catch(InterruptedException ie)
{
System.out.println(ie.toString());
}
Когда такое извещение возникает, метод run
класса ioControl выводит на консоль номер текущего
блока:
System.out.println("String = " + nPosition);
Назад Вперед |