double arrow

Синхронизация потоков

Основное различие между потоками и процессами состоит в том, что процессы защищены от воздействия друг на друга средствами операционной системы (каждый процесс выполняется в своем адресном пространстве). Использование потоков, лишенных подобной защиты, позволяет быстро запускать новые потоки и способствует их производительности. Однако здесь есть и отрицательный эффект – любой из потоков может получить доступ и даже внести изменения в данные, которые другой поток считает принадлежащими только ему. Решение этой проблемы состоит в синхронизации потоков. Ситуация, когда много потоков, обращающихся к некоторому общему ресурсу, начинают мешать друг другу, очень часта. Например, когда два потока записывают информацию в файл/объект/поток. Синхронизация кода реализуется двумя основными способами.

1. Если критическим участком является метод, то можно просто указать ключевое слово sychronized в объявлении метода, т.е.

synchronized void myMethod ()  { … }

 

Эквивалентный код можно представить в виде

void myMethod ()

{synchronized (this)

{…………….}}

 

2. Если нет доступа к классу, в котором объявлен метод, то для его синхронизации можно использовать следующий прием:

synchonized (object)

{ // операторы критического участка, в том числе и вызовы метода}

 

Здесь object – ссылка на объект, который нужно синхронизировать, т.е. на объект элементом которого является вызываемый метод. При синхронизации одного оператора фигурные скобки можно опускать.

В Java кроме использования блока synchonized разработаны и эффективные средства межпроцессового взаимодействия. Например, метод

public final void wait () throws InterruptedException;

осуществляет перевод вызывающего потока в режим ожидания, пока

некоторый другой поток не введет notify().

Существуют и другие варианты метода, например:

public final void wait (long timeout) throws InterruptedException;

 

осуществляет задержку на определенное время.

Метод

public final void notify();

 

«пробуждает» на том же объекте первый поток, который вызвал ожидание – wait()

Следующий метод

public final void notifyAll();

 

пробуждает все потоки, для которых вызван – wait() и первым будет выполняться поток с наибольшим приоритетом.

Рассмотрим несколько примеров программ с использованием потоков.

Пример 2.1 является модификацией примера апплета, приведенного в лабораторной работе №1 (см. пример 1.7). Листинг кода ниже демонстрирует, как можно нарисовать тот же домик с использованием потоков.

Пример 2.1

Листинг файла DrawHouseThreadApplet.java

 

import java.awt.*;

import java.applet.*;

 

public class DrawHouseThreadApplet extends Applet implements Runnable

{

    int level = 0;

    Thread t;

    //функция инициализации апплета

    public void init()

    {

              this.setBackground(Color. white);

              t = new Thread(this);

              t.start();

    }

    //функция перерисовки апплета

 public void paint(Graphics g)

{

g.setColor(Color.DARK_GRAY);

              if(level == 1)

              {

                       g.drawLine(50, 150, 200, 50);

                       g.drawLine(200, 50, 350, 150);

                       g.drawLine(350, 150, 50, 150);

              }

              if(level == 2)

              {

                       g.drawLine(50, 150, 200, 50);

                       g.drawLine(200, 50, 350, 150);

                       g.drawLine(350, 150, 50, 150);

                       g.drawRect(100, 150, 200, 200);

              }

              if(level == 3)

              {

                       g.drawLine(50, 150, 200, 50);

                       g.drawLine(200, 50, 350, 150);

                       g.drawLine(350, 150, 50, 150);

                       g.drawRect(100, 150, 200, 200);

                       g.drawRect(170, 200, 60, 100);

              }

              if(level == 4)

              {

                       g.drawLine(50, 150, 200, 50);

                       g.drawLine(200, 50, 350, 150);

                       g.drawLine(350, 150, 50, 150);

                       g.drawRect(100, 150, 200, 200);

                       g.drawRect(170, 200, 60, 100);

                       g.drawLine(200, 200, 200, 300);

                       g.drawLine(170, 250, 230, 250);

                       g.setColor(Color.MAGENTA);

                       g.drawString("Домик", 190, 30);

              }

    }

        

    //запуск потока

    public void run()

    {

              System.out.println("Run");

              while(true)

              {

                       level ++;

                       repaint();

                       try{

                                 Thread.currentThread().sleep(3000);

                       }

                       catch(Exception ex){}

                       if(level == 4)

                       {

                                 return;

                       }

              }

    }

}

 

Файл DrawHouseThreadApplet.html

 

<HTML>

<HEAD>

</HEAD>

<BODY bgcolor=white>

<CENTER>

<h1><font color=lightblue> <I>House</I></font></h1>

<APPLET

    code = "DrawHouseThreadApplet.class"

    width = "400"

    height    = "400"

    >

</APPLET>

</CENTER>

</BODY>

</HTML>

 

Следующий пример демонстрирует управление приоритетами.

 

Пример 2.2

Листинг TryPriorThread.java

public class TryPriorThread extends Thread{

public TryPriorThread(String threadName){

super(threadName);

System.out.println("Thread '"+threadName+"' created!");

}

public void run(){

for(int i=0;i<10;i++){

System.out.println("Thread '"+getName()+"' "+i);

try{

sleep(1); //ожидать одну миллисекунду

}

catch(InterruptedException e){

System.out.print("Error:"+e);

}

}

}

public static void main(String [ ] args){

// создать три потока выполнения

Thread min_thr = new TryPriorThread("ThreadMin");

Thread max_thr = new TryPriorThread("ThreadMax");

Thread norm_thr = new TryPriorThread("ThreadNorm");

System.out.println("Starting threads...");

min_thr.setPriority(Thread.MIN_PRIORITY); //задать потоку

                                                             //минимальный приоритет

//задать потоку максимальный приоритет

max_thr.setPriority(Thread.MAX_PRIORITY);

//задать потоку нормальный приоритет

norm_thr.setPriority(Thread.NORM_PRIORITY);

min_thr.start(); // запустить первый поток

max_thr.start(); // запустить второй поток

norm_thr.start(); // запустить третий поток

}

}

 

Результат работы программы:

Thread 'ThreadMin' created!

Thread 'ThreadMax' created!

Thread 'ThreadNorm' created!

Starting threads...

Thread 'ThreadMax' 0

Thread 'ThreadNorm' 0

Thread 'ThreadMin' 0

Thread 'ThreadMax' 1

Thread 'ThreadMax' 2

Thread 'ThreadMax' 3

Thread 'ThreadMax' 4

Thread 'ThreadMax' 5

Thread 'ThreadMax' 6

Thread 'ThreadMax' 7

Thread 'ThreadMax' 8

Thread 'ThreadMax' 9

Thread 'ThreadNorm' 1

Thread 'ThreadMin' 1

Thread 'ThreadNorm' 2

Thread 'ThreadMin' 2

Thread 'ThreadNorm' 3

Thread 'ThreadMin' 3

Thread 'ThreadNorm' 4

Thread 'ThreadMin' 4

Thread 'ThreadNorm' 5

Thread 'ThreadMin' 5

Thread 'ThreadNorm' 6

Thread 'ThreadMin' 6

Thread 'ThreadNorm' 7

Thread 'ThreadMin' 7

Thread 'ThreadNorm' 8

Thread 'ThreadMin' 8

Thread 'ThreadNorm' 9

Thread 'ThreadMin' 9

 

Пример, демонстрирующий синхронизацию доступа к файлу.

 

Пример 2.3

Листинг SynchroThreads.java

import java.io.*;

public class SynchroThreads{

public static void main(String [ ] args){

SynchroFile sf= new SynchroFile(); //объект класса SynchroFile

FileThread ft1=new FileThread("FisrtThread",sf); //первый поток

FileThread ft2=new FileThread("SecondThread",sf); //второй поток

ft1.start(); //стартовать первый поток

ft2.start(); //стартовать второй поток

}

}

class FileThread extends Thread{

String str;

SynchroFile sf;

public FileThread(String str,SynchroFile sf){

this.str=str;

this.sf=sf;

}

public void run(){

for(int i=0;i<10;i++){

sf.writing(str,i);

}

}

}

class SynchroFile{

File f=new File("file.txt");

public SynchroFile(){

System.out.println("Object SynchroFile creating...");

try{

f.delete(); //удалить файл если он есть

f.createNewFile(); //создать новый файл

}

catch(IOException ioe){

ioe.printStackTrace();

}

}

public synchronized void writing(String str,int i){

try{

RandomAccessFile raf=new RandomAccessFile(f,"rw");

raf.seek(raf.length()); //переместить указатель в конец

System.out.print(str);

raf.writeBytes(str); //записать в файл

// на случайное значение приостанавить поток

Thread.sleep((long)(Math.random()*15));

raf.seek(raf.length()); //переместить указатель в конец

System.out.print("->"+i+" \n");

raf.writeBytes("->"+i+" \n"); //записать в файл

}

catch(IOException ioe){

ioe.printStackTrace();

}

catch(InterruptedException ie){

ie.printStackTrace();

}

notify(); //известить об окончании работы с методом

}

}

 

Работа программы будет выглядеть на экране следующим образом:

Object SynchroFile creating...

FisrtThread->0

SecondThread->0

FisrtThread->1

SecondThread->1

FisrtThread->2

SecondThread->2

FisrtThread->3

SecondThread->3

FisrtThread->4

SecondThread->4

FisrtThread->5

SecondThread->5

FisrtThread->6

SecondThread->6

FisrtThread->7

SecondThread->7

FisrtThread->8

SecondThread->8

FisrtThread->9

SecondThread->9

 

В каталоге приложения будет создан файл file.txt, дублирующий информацию, выведенную на экран.

Следующий пример демонстрирует применение потоков в апплете. Создается апплет, в разных потоках осуществляется движение строки, квадрата и овала, а также зарисовка фона апплета.

 

Пример 2.4

Листинг AppletThreadSample.java

import java.awt.*;

import java.applet.*;

//создать класс апплета, который реализует интерфейс Runnable

public class AppletThreadSample extends Applet implements Runnable{

private Thread T; //создать объект потока

//объявление переменных

private ShapeString m_ShapeString = null;  //для строки

private ShapeOval m_ShapeOval = null; //для овала

private ShapeRect m_ShapeRect = null; //для квадрата

public void run() { //реализация метода run, точка входа в поток

setBackground(Color.yellow); //фон апплета зарисовывается желтым

while (true){ //бесконечный цикл

repaint(); //перерисовка апплета или вызов метода paint

try{

T.sleep(10); //приостановка апплета на 10 миллисекунл

}

catch (InterruptedException e){  }

}

}

public void init() { //метод инициализации апплета

T = new Thread(this); //создание потока и привязка его к текущему классу

T.start(); //запуск потока (вызывается run)

//создание объектов

m_ShapeString= new ShapeString();

m_ShapeOval= new ShapeOval();

m_ShapeRect= new ShapeRect();

}

public void paint(Graphics g) { //метод прорисовки апплета

//прорисовка строки

g.drawString("This is ShapeString",

m_ShapeString.x_String,m_ShapeString.y_String);

//прорисовка квадрата

g.setColor(Color.red);

g.drawRect(m_ShapeRect.x_Rect,m_ShapeRect.y_Rect,

m_ShapeRect.w_Rect,m_ShapeRect.h_Rect);

//прорисовка овала

g.setColor(Color.CYAN);

g.fillOval(m_ShapeOval.x_Oval,m_ShapeOval.y_Oval,

m_ShapeOval.w_Oval,m_ShapeOval.h_Oval);

}      

//класс ShapeString реализующий интерфейс Runnable

class ShapeString implements Runnable{

Thread T;

int x_String, y_String; //координаты строки

public ShapeString(){ //конструктор

T = new Thread(this); //создание объекта Thread

//установление начальных координат строки

x_String=100; y_String=100;

T.start(); //запуск потока (вызов метода run)

}

public void run(){ //метод run

for(;;){

x_String+=15; //изменение координаты строки

try{

T.sleep(1000); //приостановка работы потока на 1000 миллисекунд

}

catch (InterruptedException e){}

}

}

}

//класс ShapeRect реализующий интерфейс Runnable

class ShapeRect implements Runnable{

Thread T;

int x_Rect,y_Rect,w_Rect,h_Rect; //координаты и размеры квадрата

public ShapeRect(){ //конструктор

T = new Thread(this); //создание объекта Thread

//установление начальных координат квадрата

x_Rect=350;y_Rect=50;w_Rect=100;h_Rect=100;

T.start();//запуск потока (вызов метода run)

}

public void run(){ //метод run

for(;;){ 

x_Rect-=15;  //изменение координаты квадрата

try{

T.sleep(500);  //приостановка работы потока на 1000 миллисекунд

}

catch (InterruptedException e){}

}

}

}

//класс ShapeOval реализующий интерфейс Runnable

class ShapeOval implements Runnable{

Thread T;

int x_Oval, y_Oval,w_Oval,h_Oval; //координаты и размеры овала

public ShapeOval(){ //конструктор

T = new Thread(this); //создание объекта Thread

//установление начальных координат овала

x_Oval=30; y_Oval=30;w_Oval=100;h_Oval=90;

T.start(); //запуск потока (вызов метода run)

}

public void run(){//метод run

for(;;){//изменение координат овала

x_Oval+=8;

y_Oval+=7;

try{

T.sleep(100); //приостановка работы потока на 100 миллисекунд

}

catch (InterruptedException e){  }                    

} } } }

Откомпилируйте   программу.  Не   забудьте  создать   соответствующий

html -файл. Результат работы программы показан на рис. 2.2.

 

 

Рис. 2.2. Результат работы программы AppletThreadSample.java

 

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



Сейчас читают про: