[英]Fast food program, multiple threads and semaphores Java
因此,我在編程過程中偶然偶然發現了Semaphores ,最近,我親自設計了一個復制快餐鏈的程序。
我將盡力解釋,如果需要更詳盡的說明,請在評論部分告訴我
計划:
有一個生產者和一個消費者 (耕作者和工人),所以耕作者接受訂單(將它們放在緩沖區中,從而形成一個圓形數組..),然后工人處理該訂單(從循環數組中取出訂單)
我正在嘗試實現信號量,以便在下達訂單后,直到工作人員處理完該訂單后,才能使用該特定的直到。 並且還使用信號量,以便警官一次只能取出一個命令
這是主要方法:
public class FastFood {
/**
* @param args the command line arguments
*/
static Buffer buff = new Buffer(2);
static Semaphore semWorker = new Semaphore(1);
static Semaphore semTills = new Semaphore(1);
static int totalOrders = 10;
static int startOrders = 0;
static int processedOrders = 0;
public static void main(String[] args) {
// TODO code application logic here
int numberOfWorkers = 2;
int numberOfTills = 3;
int numberOfFoodChoices = 4;
Random rand = new Random();
Tills[] tills = new Tills[numberOfTills];
Worker[] workers = new Worker[numberOfWorkers];
//int tillId, int foodId, Buffer buff
for (int i = 0; i < tills.length; i++) {
int foodId = rand.nextInt(numberOfFoodChoices) + 1;
tills[i] = new Tills(i, foodId, buff);
tills[i].start();
}
//int workerId, Buffer buff
for (int i = 0; i < workers.length; i++) {
workers[i] = new Worker(i, buff);
workers[i].start();
}
for (Tills till : tills) {
try {
till.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
for (Worker worker : workers) {
try {
worker.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
因此,正如您可以通過main方法看到的那樣,我正在循環並運行Worker和Tills的線程數組。
這是耕種課程。 因此,這將創建訂單。 您將能夠看到我正在使用FastFood.semTills.down()和FastFood.semTills.up()這是使用信號量。 因此,向下獲取信號量,向上釋放它。 但是 ,問題是我對這些信號起伏的定位邏輯。
public class Tills extends Thread {
private final Buffer buff;
private final int foodId;
private final int tillId;
public Tills(int tillId, int foodId, Buffer buff) {
this.tillId = tillId;
this.foodId = foodId;
this.buff = buff;
}
@Override
public void run(){
FastFood.semTills.down();
while(FastFood.startOrders < FastFood.totalOrders){
FastFood.semTills.up();
buff.acquire();
while(buff.isFull()){
try {
buff.release();
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Tills.class.getName()).log(Level.SEVERE, null, ex);
}
}
FastFood.startOrders++;
Order v = new Order(foodId, tillId);
System.out.println(v.toString());
try {
Random n = new Random();
int time = n.nextInt(100) + 1;
buff.release();
sleep(time);
buff.insert(v);
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
worker類有點相同,但是我想確保一次只有一個worker可以處理一個特定的訂單(允許多個worker啟用多個訂單是可以的)
public class Worker extends Thread{
private final int workerId;
private final Buffer buff;
public Worker(int workerId, Buffer buff) {
this.workerId = workerId;
this.buff = buff;
}
public void run(){
FastFood.semWorker.down();
while(FastFood.totalOrders>FastFood.processedOrders){
buff.acquire();
while(buff.isEmpty()){
FastFood.semWorker.up();
try {
buff.release();
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
FastFood.processedOrders++;
System.out.print("Worker: " + workerId);
buff.remove();
buff.release();
try {
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
FastFood.semWorker.up();
}
這是我得到的輸出,您可以看到它不等待訂單被處理,因此信號量的位置一定是錯誤的,我嘗試了各種可能性:
run:
FoodId: 3 TillId : 1 Order Count : 0
FoodId: 3 TillId : 0 Order Count : 1
FoodId: 4 TillId : 2 Order Count : 2
FoodId: 4 TillId : 2 Order Count : 3
FoodId: 4 TillId : 2 Order Count : 4
FoodId: 3 TillId : 0 Order Count : 5
FoodId: 3 TillId : 0 Order Count : 6
Worker: 1 Food: 3 TillId: 0
Worker: 0 Food: 3 TillId: 0
FoodId: 3 TillId : 0 Order Count : 7
FoodId: 3 TillId : 0 Order Count : 8
Worker: 1 Food: 3 TillId: 0
FoodId: 3 TillId : 0 Order Count : 9
FoodId: 3 TillId : 1 Order Count : 10
Worker: 0 Food: 3 TillId: 0
Worker: 1 Food: 3 TillId: 1
Worker: 0 Food: 3 TillId: 0
Worker: 1 Food: 4 TillId: 2
Worker: 0 Food: 3 TillId: 0
Worker: 1 Food: 4 TillId: 2
Worker: 0 Food: 3 TillId: 0
10
快速課程簡介:
FastFood:Main,創建線程Buffer:對於圓形數組Order:存儲直到蒂爾為止的食物:創建訂單Worker:處理訂單
信號:
package fastfood;
public class Semaphore {
private int count;
public Semaphore(int n) {
count = n;
}
public synchronized void down() {
while (count == 0) {
try {
wait(); // Blocking call.
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
count--;
}
public synchronized void up() {
count++;
notify();
}
}
緩沖:
public class Buffer {
private int size;
private int inPtr = 0;
private int outPtr = 0;
private int counter = 0;
private Order[] data;
private Semaphore sem = new Semaphore(1);
public Buffer(int size) {
this.size = size;
this.data = new Order[size];
}
public Order remove(){
// removes the revote for the officer
Order out;
out = data[outPtr];
System.out.println(" Food: " + out.getFoodId() + " TillId: " +
out.getTillId());
outPtr = (outPtr+1)%size;
counter--;
return out;
}
public void insert(Order i){
// inserts a new vote
data[inPtr] = i;
inPtr = (inPtr+1)%size;
counter++;
}
public boolean isEmpty(){
// returns true if empty
return counter==0;
}
public boolean isFull(){
// returns true if full
return counter==size;
}
public void acquire(){
sem.down();
}
public void release(){
sem.up();
}
}
變化:
變更2:
工人階級:
public void run() {
while(FastFood.processedOrders < FastFood.totalOrders){
try{
buff.acquire();
FastFood.semWorker.down();
while(buff.isEmpty()){
try {
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
try{
Order o = buff.remove();
System.out.println(o.toString() + " FoodId: " + o.getFoodId()
+ " TillId: " + o.getTillId());
FastFood.processedOrders++;
}catch(Exception e){
System.out.println(e);
}
}finally{
buff.release();
FastFood.semTills.up();
}
}
耕種班:
while (FastFood.startOrders < FastFood.totalOrders) {
try {
buff.acquire();
FastFood.semTills.down();
while (buff.isFull()) {
try {
sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Tills.class.getName()).log(Level.SEVERE, null, ex);
}
}
try {
Order o = new Order(foodId, tillId);
System.out.println(o.toString());
buff.insert(o);
FastFood.startOrders++;
} catch (Exception e) {
System.out.println(e);
}
} finally {
buff.release();
FastFood.semWorker.up();
}
訂購:
public class Order {
private final int foodId;
private final int tillId;
static int count = 0;
private int orderCount=0;
public Order(int foodId, int tillId){
this.foodId = foodId;
this.tillId = tillId;
this.orderCount = count++;
}
public int getFoodId() {
return foodId;
}
public int getTillId() {
return tillId;
}
public static int getCount() {
return count;
}
public int getOrderCount() {
return orderCount;
}
@Override
public String toString() {
return "FoodId: " +foodId+" TillId : "+tillId+" Order Count : "+ orderCount;
}
有什么原因使您在while循環后立即釋放鎖? 在您檢查while循環后,您的耕作就會立即釋放。 這似乎很令人困惑。 您想讓耕種線程在命令完成后進入休眠狀態,並且僅在其命令完成后才被喚醒嗎? 您是否希望您的工人只按特定順序工作? 只要緩沖區中有要等待的訂單,工人就不應該能夠處理任何訂單嗎? 抱歉,我沒有50位代表,因此無法發表評論。
快餐
import java.util.*;
public class FastFood {
/**
* @param args the command line arguments
*/
static Buffer buff = new Buffer(2);
static Semaphore semWorker = new Semaphore(2);
static Semaphore semTills = new Semaphore(2);
static int totalOrders = 10;
static int startOrders = 0;
static int processedOrders = 0;
public static void main(String[] args) {
// TODO code application logic here
int numberOfWorkers = 2;
int numberOfTills = 3;
int numberOfFoodChoices =4;
Random rand = new Random();
Tills[] tills = new Tills[numberOfTills];
Worker[] workers = new Worker[numberOfWorkers];
//int tillId, int foodId, Buffer buff
for (int i = 0; i < tills.length; i++) {
int foodId = rand.nextInt(numberOfFoodChoices) + 1;
tills[i] = new Tills(i, foodId, buff);
tills[i].start();
}
//int workerId, Buffer buff
for (int i = 0; i < workers.length; i++) {
workers[i] = new Worker(i, buff);
workers[i].start();
}
for (Tills till : tills) {
try {
till.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
for (Worker worker : workers) {
try {
worker.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
}
緩沖
public class Buffer {
private int size;
private int inPtr = 0;
private int outPtr = 0;
private int counter = 0;
private Order[] data;
public Buffer(int size) {
this.size = size;
this.data = new Order[size];
}
public synchronized String remove(){
// removes the revote for the officer
Order out;
out = data[outPtr];
outPtr = (outPtr+1)%size;
counter--;
return " Food: " + out.getFoodId() + " ordered by TillId: " + out.getTillId();
}
public synchronized void insert(Order i){
// inserts a new vote
data[inPtr] = i;
inPtr = (inPtr+1)%size;
counter++;
}
public synchronized boolean isEmpty(){
// returns true if empty
return counter==0;
}
public synchronized boolean isFull(){
// returns true if full
return counter==size;
}
}
冰磧
public class Tills extends Thread {
private final Buffer buff;
private final int foodId;
private final int tillId;
public Tills(int tillId, int foodId, Buffer buff) {
this.tillId = tillId;
this.foodId = foodId;
this.buff = buff;
}
public void run(){
while(FastFood.startOrders < FastFood.totalOrders){
FastFood.semTills.down();
while(buff.isFull()){
try {
sleep(100);
} catch (InterruptedException ex) {
//Logger.getLogger(Tills.class.getName()).log(Level.SEVERE, null, ex);
}
}
FastFood.startOrders++;
Order v = new Order(foodId, tillId);
buff.insert(v);
System.out.println("Till number " + tillId + " created a new order " + foodId + " to be processed");
FastFood.semWorker.up();
}
}
}
工人
public class Worker extends Thread{
private final int workerId;
private final Buffer buff;
public Worker(int workerId, Buffer buff) {
this.workerId = workerId;
this.buff = buff;
}
public void run() {
while (FastFood.totalOrders > FastFood.processedOrders) {
FastFood.semWorker.down();
while (buff.isEmpty()) {
try {
sleep(100);
} catch (InterruptedException ex) {
//Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
//FastFood.processedOrders++;
System.out.println("Worker: " + workerId + " completed order number " + buff.remove() + " total orders processed so far: " + FastFood.processedOrders++);
FastFood.semTills.up();
try {
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
} catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
}
不知道這是否像您的訂單類
public class Order{
private int tillID;
private int foodID;
public Order(int food, int till){
tillID = till;
foodID = food;
}
int getFoodId(){
return foodID;
}
int getTillId(){
return tillID;
}
}
在嘗試之前,請注意這不是100%正確的。 我擺脫了緩沖區中的信號量,只是使方法同步。 如果您在快餐中將信號量值更改為1,它將無法完全運行,因為並非所有線程都能喚醒,並在最后退出以退出程序。
另外,對於totalOrders和processesOrders使用靜態變量作為控制線程何時停止運行的方式似乎也令人擔憂,因為我認為線程每個都有自己的副本,因此也可能導致爭用情況。 我可能是錯的。 我不確定您還沒有看過什么,但是我認為這提供了一些有用的信息,可能會有所幫助
當Worker
等待命令時,它實際上等待以下事件之一 :
buffer
變為非空。 Tills
完成了工作,因此不會產生任何訂單。 最好將所有這些事件結合到單個保護中(在您的情況下為Semaphore
)。 並將wait實現移到Buffer
類中:
public class Buffer {
private int size;
private int inPtr = 0;
private int outPtr = 0;
private int counter = 0;
private Order[] data;
private Semaphore sem = new Semaphore(1);
private bool isFinished; /* Whether no futher Orders will be added */
public Buffer(int size) {
this.size = size;
this.data = new Order[size];
}
/* Put Order into buffer. If buffer is full, wait.*/
public void put(Order i){
sem.down();
while(counter == size) // Buffer is full
{
// Some sort of busy wait
sem.up();
sleep(100);
sem.down();
}
data[inPtr] = i;
inPtr = (inPtr+1)%size;
counter++;
sem.up();
}
/*
* Get order from the buffer and remove it.
* If buffer is empty and not finished, wait.
* Return null if buffer is empty and finished.
*/
public Order get(){
sem.down();
while(counter == 0 && !isFinished)
{
// Some sort of busy wait
sem.up();
sleep(100);
sem.down();
}
Order out;
if(counter) // Otherwise `isFinished` is set and null should be returned.
{
out = data[outPtr];
System.out.println(" Food: " + out.getFoodId() + " TillId: " +
out.getTillId());
outPtr = (outPtr+1)%size;
counter--;
}
sem.up();
return out;
}
/* Mark buffer as finished. */
void finish(void)
{
sem.down();
isFinished = true;
sem.up();
}
}
請注意,如何執行繁忙的等待:釋放信號量,線程休眠一段時間,然后再次獲取信號量。
接下來,最好將有關officer
所有邏輯合並到單獨的類中。 這很簡單,但是會消耗semTills
全局信號量:
class Officer
{
private int totalOrders;
private int startOrder;
private Semaphore sem = new Semaphore(1);
public Officer(int totalOrders) {
this.totalOrders = totalOrders;
}
/* Return true if order is allowed, false otherwise. */
public bool getOrder(void) {
bool result;
sem.down();
if(startOrders != totalOrders) {
startOrders++;
result = true;
}
sem.up();
return result;
}
}
如您所見,檢查startOrders
並對其進行修改應該位於單個關鍵部分下。
接下來,在處理Tills
產生的order時 , Tills
應等待。 可以使用最初鎖定的Semaphore
來實現這樣的一鍵式等待:
public class Tills extends Thread {
private final Buffer buff;
private final int foodId;
private final int tillId;
private final Semaphore waiter = Semaphore(0); // Initially locked!
public Tills(int tillId, int foodId, Buffer buff) {
this.tillId = tillId;
this.foodId = foodId;
this.buff = buff;
}
@Override
public void run(){
while(FastFood.officer.getOrder()){
Order v = new Order(foodId, tillId);
System.out.println(v.toString());
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
buff.put(v);
//Wait order to be processed
waiter.down();
}
}
/* Tell that order, added to the buffer, is processed. */
public markOrderProcessed(void) {
waiter.up();
}
}
注意,實現變得更加簡單。 類markOrderProcessed
發布方法markOrderProcessed
以供Worker
調用。
由於worker僅從緩沖區獲取訂單 ,因此該對象(類型為Order
)應包含對Tills
引用,並創建該對象。 而且,現在可以同時創建Order
對象。 因此,其靜態count
字段應受Semaphore
保護。
public class Order {
...
private Tills till; // New member
static private Semaphore sem = Semaphore(1); // Protect *count* field
public Order(..., Tills till) { // New parameter to the constructor
...
this.till = till;
// `count` should be incremented under protection.
sem.down();
this.orderCount = count++;
sem.up();
}
/* New method: mark order as processed. */
public markProcessed(void)
{
till.markOrderProcessed();
}
}
現在,將讀取所有有關工具Worker
的run方法的信息。 注意,現在此方法不直接使用任何同步,所有操作都在低級類中完成:
public void run(){
Order order;
while((order = buff.get()) != null) {
System.out.println("Worker: " + workerId + " " + order);
Random n = new Random();
int time = n.nextInt(100) + 1;
sleep(time);
order.markProcessed(); // Mark order as processed, so tills can continue.
}
}
和主班。 請注意,只有在所有Tills
完成(加入)后,該緩沖區才標記為完成:
public class FastFood {
static Buffer buff = new Buffer(2);
static Officer officer = new Officer(10);
public static void main(String[] args) {
// TODO code application logic here
int numberOfWorkers = 2;
int numberOfTills = 3;
int numberOfFoodChoices = 4;
Random rand = new Random();
Tills[] tills = new Tills[numberOfTills];
Worker[] workers = new Worker[numberOfWorkers];
//int tillId, int foodId, Buffer buff
for (int i = 0; i < tills.length; i++) {
int foodId = rand.nextInt(numberOfFoodChoices) + 1;
tills[i] = new Tills(i, foodId, buff);
tills[i].start();
}
//int workerId, Buffer buff
for (int i = 0; i < workers.length; i++) {
workers[i] = new Worker(i, buff);
workers[i].start();
}
for (Tills till : tills) {
try {
till.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
/*
* Mark buffer as finished.
*
* Workers, which found the buffer empty, may safetly stop now.
*/
buff.finish();
for (Worker worker : workers) {
try {
worker.join();
}catch (InterruptedException ex) {
System.out.println(ex);
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.