Семафоры используются для того, чтоб перед использованием ресурса проверить его доступность.
Примером из жизни может служить тележка (общий ресурс) и два работника (потоки java). Один работник, к примеру, наполняет тележку песком. В это время второй работник, который перевозит груз и затем разгружает, не может взять тележку и отвезти ее.
В то же время, если второй работник увез тележку, то первый работник не должен ничего наполнять.
Ниже приведен небольшой пример программы из 3 классов.
1. Главнй класс
2. Класс тележки
3. Класс работника (boolean переменная определяет это работник по загрузке тележки либо по разгрузке)
Данный пример не гарантирует, что наполнение тележки произойдет раньше, чем ее разгрузка. Если есть такая необходимость, то одни из способов - это поставить задержку после создания первого потока Worker.
Результат работы программы:
Примером из жизни может служить тележка (общий ресурс) и два работника (потоки java). Один работник, к примеру, наполняет тележку песком. В это время второй работник, который перевозит груз и затем разгружает, не может взять тележку и отвезти ее.
В то же время, если второй работник увез тележку, то первый работник не должен ничего наполнять.
Ниже приведен небольшой пример программы из 3 классов.
1. Главнй класс
import java.util.concurrent.Semaphore; public class SemaphoreExample { public static void main(String[] args) { Semaphore semaphore = new Semaphore(1); new Worker(semaphore, "Adder", true).start(); new Worker(semaphore, "Reducer", false).start(); } }
2. Класс тележки
public class Cart { private static int weight = 0; public static void addWeight(){ weight--; } public static void reduceWeight(){ weight++; } public static int getWaight(){ return weight; } }
3. Класс работника (boolean переменная определяет это работник по загрузке тележки либо по разгрузке)
import java.util.concurrent.Semaphore; public class Worker extends Thread { private Semaphore semaphore; private String workerName; private boolean isAdder; public Worker(Semaphore semaphore, String workerName, boolean isAdder) { this.semaphore = semaphore; this.workerName = workerName; this.isAdder = isAdder; } @Override public void run() { System.out.println(workerName + " started working..."); try { System.out.println(workerName + " waiting for cart..."); semaphore.acquire(); System.out.println(workerName + " got access to cart..."); for (int i = 0 ; i < 10 ; i++) { if (isAdder) Cart.reduceWeight(); else Cart.addWeight(); System.out.println(workerName + " changed weight to: " + Cart.getWaight()); Thread.sleep(10L); } System.out.println(workerName + " finished working with cart..."); } catch (Exception e) { e.printStackTrace(System.err); } finally {
semaphore.release();
}
} }
Данный пример не гарантирует, что наполнение тележки произойдет раньше, чем ее разгрузка. Если есть такая необходимость, то одни из способов - это поставить задержку после создания первого потока Worker.
Результат работы программы:
Adder started working... Adder waiting for cart... Adder got access to cart... Adder changed weight to: 1 Reducer started working... Reducer waiting for cart... Adder changed weight to: 2 Adder changed weight to: 3 Adder changed weight to: 4 Adder changed weight to: 5 Adder changed weight to: 6 Adder changed weight to: 7 Adder changed weight to: 8 Adder changed weight to: 9 Adder changed weight to: 10 Adder finished working with cart... Reducer got access to cart... Reducer changed weight to: 9 Reducer changed weight to: 8 Reducer changed weight to: 7 Reducer changed weight to: 6 Reducer changed weight to: 5 Reducer changed weight to: 4 Reducer changed weight to: 3 Reducer changed weight to: 2 Reducer changed weight to: 1 Reducer changed weight to: 0 Reducer finished working with cart...
Спасибо за интересную статью, коротко и ясно.
ОтветитьУдалитьЯ собираюсь разработать проект на подобие онлайн видео компрессинг. Как мы знаем оперция компрессинга занимает не малых ресурсов процессора. И я решил разбить запросы на компрессинг по 10 потоков, как вы думаете парвильно ли будет в данном случае использовать шаблон семафоры?
Здравствуйте, спасибо за комментарий.
УдалитьЯ не знаком с задачей компрессинга, но опишу несколько проблем многопоточности:
1. Выполнение многошаговой большой задачи, путем разбиения на мелкие. Но при условии, что мы не можем начать новый шаг, если предыдущий шаг не выполнен полностью. В данном случае подойдут "Защелки" http://movejava.blogspot.com/2013/06/javautilconcurrentcountdownlatch.html
2. Если у нас в многопоточной среде идет работа с ресурсом, который может работать в однопоточном режиме (как пример), то тогда нужно использовать Семафоры.
Не будет ли лучше перенести semaphore.release() в блок finally? Если Вы учли возможность появления Exception, то ресурс лучше освободить в любом случае.
ОтветитьУдалитьВсе верно, спасибо за замечание.
ОтветитьУдалить