В этой статье я постараюсь максимально просто и понятно изложить основы
программирования на языке Delphi
Java для чайников. Урок 5. Потоки (awt,
thread).
Посмотреть пример апплета, разработанного на данном уроке, можно
здесь.
Сегодня мы изучим понятие потока. Дело в том, что под операционной системой
Windows может одновременно выполнятся несколько задач.
На самом дел, конечно, одновременность кажущаяся. Каждая задача выполнятся
кусочками по очереди, но так как это происходит очень быстро, то кажется, что
программы выполняются одновременно. Java так же
поддерживает многозадачность. Для этого в ней существует такой класс, как
Theard - поток. В программе можно запустить несколько
потоков, выполняющихся одновременно. Например, пока пользователь что то
набирает, можно в фоновом режиме запустить какой нибудь длительный процесс
расчета или загрузки данных.
Для демонстрации работы с потоками рассмотрим пример программы "гонки". В
качестве гоночной дорожки у нас будут черны линии, а в качестве гонщиков -
желтые овалы:
Каждый гонщик - это отдельный поток. Гонщика реализует класс
Racer, объявленный от интерфейса Runnable:
import
java.awt.Graphics;
import
java.awt.Color;
//класс поточный, поэтому он должен наследовать интерфейс Runnable
public
class
Racer
extends
java.awt.Canvas
implements
Runnable
{
int
position=0;
String name;
int
stepsCount=600;
boolean
isFinished;
//флаг, что гонщик пришел на финиш
//конструктор класса
//экземпляр класса создаем по имени
public
Racer
(String
aName)
{
name=aName;
}
public
synchronized
void
paint(Graphics
g)
{
//рисуем
дорожку
g.setColor(Color.black);
g.drawLine(0,size().height/2,size().width-170,size().height/2);
//рисуем гонщика в виде желтого овала
if(isFinished)
g.setColor(Color.green);
else
g.setColor(Color.yellow);
g.fillOval(position*(size().width-170)/stepsCount,0,15,size().height);
//если гонщик пришел на финиш, выдадим об этом сообщение
if(isFinished)
{
g.setColor(Color.red);
g.drawString("Racer
'"+name+"'
is finished",size().width-150,size().height);
}
g.setColor(Color.red);
g.drawString(""+position,0,size().height);
}
public
void
run()
{
isFinished=false;
//цикл
до конца гонки
while(position<stepsCount)
{
position++;
repaint();
//останавливаем поток, что бы началось отображение
try
{
Thread.currentThread().sleep(10);
}
catch
(Exception
e)
{System.out.println("Exception
on sleep");}
}
isFinished=true;
repaint();
}
} |
Класс,
который будет выполнятся в потоке, должен иметь метод run,
которым его запускают в режим поточного выполнения. Разные потоки могут
обращаться к одним и тем же данным. Дабы избежать конфликта, в
Java используется синхронизация.
Синхронизировать можно метод, объявив его с модификатором synchronized, либо
целый блок, так же используя ключевое слово synchronized. Сегодня мы подробно на
синхронизации останавливается не будем, так как цель настоящего урока дать
общие представления о потоках. Но к многопоточным приложениям мы еще вернемся и
разберем их более подробно. А сейчас рассмотрим некоторые методы класса
Thread:
-
start(). Запускает поток на выполнение.
-
stop(). Заканчивает выполнение потока.
-
sleep(long
msec). Останавливает выполнение потока на
указанное количество миллисекунд.
-
yield().
Передает ресурсов процессора другому потоку.
-
suspend().
Приостанавливает выполнение потока.
-
resume().
Возобновляет выполнение потока.
В нашем
примере мы используем метод sleep, что бы
приостановить на время выполнение потока, дав возможность перерисовать картинку
на экране. Если этого не делать, программа просто повиснет.
И так, с
классом "гонщика" понятно, тем более в тексте программы есть комментарии.
Поэтому переходим к основному классу програмы:
import
java.awt.Graphics;
import
java.awt.GridLayout;
public
class
Race
extends
java.applet.Applet
implements
Runnable
{
Racer theRacers[];
//массив
гонщиков
static
int
count=3;
//количество
гонщиков
Thread theThreads[];
//массив потоков
Thread thisThread;
//основной поток (поток, который управляет другими потоками)
static
boolean
inApplet=true;
int
numberOfTheradAtStart;
//в конце программы при помощи этой переменной
//определяем, все ли потоки прекратили существование
public
void
init()
{
numberOfTheradAtStart=Thread.activeCount();
//определеяем расположение гонщиков в окне,
//они будут добавлятся один за друим
setLayout(new
GridLayout(count,1));
//создаем массивы гонщиков и потоков
theRacers=new
Racer[count];
theThreads=new
Thread[count];
//в цикле создаем каждый элемент массивов
for(int
i=0;i<count;i++)
{
theRacers[i]=new
Racer("Racer
"+i);
theRacers[i].resize(size().width,size().height/count);
add(theRacers[i]);
theThreads[i]=new
Thread(theRacers[i]);
}
}
//этой процедурой мы запускаем все потоки
public
void
start()
{
for(int
i=0;i<count;i++)
theThreads[i].start();
thisThread=new
Thread(this);
}
public
void
stop()
{
thisThread.stop();
}
public
void
run()
{
// цикл, пока все гонщики не закончат гонку
while(Thread.activeCount()>numberOfTheradAtStart+2)
{
try
{
thisThread.sleep(100);
}
catch
(InterruptedException
e)
{
System.out.println("thisTread
was interruped");
}
}
if(inApplet)
{
stop();
destroy();
}
else
System.exit(0);
}
public
static
void
main(String
argv[])
{
inApplet=false;
Race theRace=new
Race();
theRace.init();
theRace.start();
}
} |
Работает
эта программа следующим образом:
1. Создает
экземпляр самой себя.
2.
Вызывает у этого экземпляра процедуру инициализации, в которой создает массив
гонщиков и массив потоков. В цикле заполняет эти массивы.
3.
Вызывает процедуру start(),
в которой запускает потоки для гонщиков, затем создает и запускает поток для
самой себя.
Далее все
работает в потоках. У всех объектов периодически вызывается метод
run. В том числе и у главного класса. В этом случае
проихсодит проверка, все ли потоки завершены, если нет - поток так же на
некоторое время останавливает себя, дав другим потокам поработать. Если все
гонщики пришли на финиш:
то главный
поток завершает самого себя.
(С)
Шуравин Александр
|