Семафоры

Последнее обновление: 28.04.2018

Семафоры представляют еще одно средство синхронизации для доступа к ресурсу. В Java семафоры представлены классом Semaphore, который располагается в пакете java.util.concurrent.

Для управления доступом к ресурсу семафор использует счетчик, представляющий количество разрешений. Если значение счетчика больше нуля, то поток получает доступ к ресурсу, при этом счетчик уменьшается на единицу. После окончания работы с ресурсом поток освобождает семафор, и счетчик увеличивается на единицу. Если же счетчик равен нулю, то поток блокируется и ждет, пока не получит разрешение от семафора.

Установить количество разрешений для доступа к ресурсу можно с помощью конструкторов класса Semaphore:

Semaphore(int permits)
Semaphore(int permits, boolean fair)

Параметр permits указывает на количество допустимых разрешений для доступа к ресурсу. Параметр fair во втором конструкторе позволяет установить очередность получения доступа. Если он равен true, то разрешения будут предоставляться ожидающим потокам в том порядке, в каком они запрашивали доступ. Если же он равен false, то разрешения будут предоставляться в неопределенном порядке.

Для получения разрешения у семафора надо вызвать метод acquire(), который имеет две формы:

void acquire() throws InterruptedException
void acquire(int permits) throws InterruptedException

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

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

После окончания работы с ресурсом полученное ранее разрешение надо освободить с помощью метода release():

void release()
void release(int permits)

Первый вариант метода освобождает одно разрешение, а второй вариант - количество разрешений, указанных в permits.

Используем семафор в простом примере:

import java.util.concurrent.Semaphore;

public class Program {

    public static void main(String[] args) {
        
        Semaphore sem = new Semaphore(1); // 1 разрешение
        CommonResource res = new CommonResource();
        new Thread(new CountThread(res, sem, "CountThread 1")).start();
        new Thread(new CountThread(res, sem, "CountThread 2")).start();
        new Thread(new CountThread(res, sem, "CountThread 3")).start();
    }
}
class CommonResource{
    
    int x=0;  
}

class CountThread implements Runnable{

    CommonResource res;
    Semaphore sem;
    String name;
    CountThread(CommonResource res, Semaphore sem, String name){
        this.res=res;
        this.sem=sem;
        this.name=name;
    }
     
    public void run(){
        
        try{
            System.out.println(name + " ожидает разрешение");
            sem.acquire();
            res.x=1;
            for (int i = 1; i < 5; i++){
                System.out.println(this.name + ": " + res.x);
                res.x++;
                Thread.sleep(100);
            }
        }
        catch(InterruptedException e){System.out.println(e.getMessage());}
        System.out.println(name + " освобождает разрешение");
        sem.release();
    }
}

Итак, здесь есть общий ресурс CommonResource с полем x, которое изменяется каждым потоком. Потоки представлены классом CountThread, который получает семафор и выполняет некоторые действия над ресурсом. В основном классе программы эти потоки запускаются. В итоге мы получим следующий вывод:

CountThread 1 ожидает разрешение
CountThread 2 ожидает разрешение
CountThread 3 ожидает разрешение
CountThread 1: 1
CountThread 1: 2
CountThread 1: 3
CountThread 1: 4
CountThread 1 освобождает разрешение
CountThread 3: 1
CountThread 3: 2
CountThread 3: 3
CountThread 3: 4
CountThread 3 освобождает разрешение
CountThread 2: 1
CountThread 2: 2
CountThread 2: 3
CountThread 2: 4
CountThread 2 освобождает разрешение

Семафоры отлично подходят для решения задач, где надо ограничивать доступ. Например, классическая задача про обедающих философов. Ее суть: есть несколько философов, допустим, пять, но одновременно за столом могут сидеть не более двух. И надо, чтобы все философы пообедали, но при этом не возникло взаимоблокировки философами друг друга в борьбе за тарелку и вилку:

import java.util.concurrent.Semaphore;

public class Program {

    public static void main(String[] args) {
        
        Semaphore sem = new Semaphore(2);
		for(int i=1;i<6;i++)
			new Philosopher(sem,i).start();
    }
}
// класс философа
class Philosopher extends Thread 
{
    Semaphore sem; // семафор. ограничивающий число философов
    // кол-во приемов пищи
    int num = 0;
    // условный номер философа
    int id;
    // в качестве параметров конструктора передаем идентификатор философа и семафор
    Philosopher(Semaphore sem, int id)
    {
		this.sem=sem;
		this.id=id;
    }
    
    public void run()
    {
		try
		{
			while(num<3)// пока количество приемов пищи не достигнет 3
			{
				//Запрашиваем у семафора разрешение на выполнение
				sem.acquire(); 
				System.out.println ("Философ " + id+" садится за стол");
				// философ ест
				sleep(500);
				num++;
					
				System.out.println ("Философ " + id+" выходит из-за стола");
				sem.release();
			
				// философ гуляет
				sleep(500);
			}
		}
		catch(InterruptedException e)
		{
			System.out.println ("у философа " + id + " проблемы со здоровьем");
		}
    }
}

В итоге только два философа смогут одновременно находиться за столом, а другие будут ждать:

Философ 1 садится за стол
Философ 3 садится за стол
Философ 3 выходит из-за стола
Философ 1 выходит из-за стола
Философ 2 садится за стол
Философ 4 садится за стол
Философ 2 выходит из-за стола
Философ 4 выходит из-за стола
Философ 5 садится за стол
Философ 1 садится за стол
Философ 1 выходит из-за стола
Философ 5 выходит из-за стола
Философ 3 садится за стол
Философ 2 садится за стол
Философ 3 выходит из-за стола
Философ 4 садится за стол
Философ 2 выходит из-за стола
Философ 5 садится за стол
Философ 4 выходит из-за стола
Философ 5 выходит из-за стола
Философ 1 садится за стол
Философ 3 садится за стол
Философ 1 выходит из-за стола
Философ 2 садится за стол
Философ 3 выходит из-за стола
Философ 5 садится за стол
Философ 2 выходит из-за стола
Философ 4 садится за стол
Философ 5 выходит из-за стола
Философ 4 выходит из-за стола
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850