簡體   English   中英

快餐程序,多個線程和信號量Java

[英]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等待命令時,它實際上等待以下事件之一

  1. buffer變為非空。
  2. 所有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();
    }
}

現在,將讀取所有有關工具Workerrun方法的信息。 注意,現在此方法不直接使用任何同步,所有操作都在低級類中完成:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM