快餐程序,多线程和信号量Java

时间:2015-12-17 00:41:19

标签: java multithreading

所以我在编程过程中偶然发现了信号量,并且最近我自己设计了一个复制快餐连锁店的程序。

我将尽我所能尽力解释,如果我需要更加透彻,请在评论部分告诉我

计划:

有一个 生产者 和一个 消费者 (tills和worker)所以tills接受订单(Place他们在一个缓冲区,所以一个圆形数组...)和工人处理该顺序(从圆形数组中取出订单)

我正在尝试实现信号量,以便在订单生成时,在工人处理订单之前,不能使用该特定的信号。并且还使用信号量,以便官员一次只能取出一个订单

以下是主要方法:

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的线程数组。

这是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);
            }

        }
    }

工人类有点相同,但我想确保一次只能处理一个特定订单的工人(可以让多个工人启用多个订单)

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,创建线程 缓冲区:用于圆形阵列 订购:存储什么食物直到什么 Tills:创建订单 工人:处理订单

信号量:

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();
           }

       }

Tills Class:

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; 
    }

2 个答案:

答案 0 :(得分:1)

你有没有理由在你的while循环后立即释放你的锁?在您检查while循环后,您的tills正在释放它们。这看起来很混乱。你希望你的直销线程在订单完成后才能入睡,只有在订单完成后才会被唤醒?而且您希望您的员工只专门处理某个订单?只要在缓冲区中有等待命令,工人是否应该能够处理任何订单?对不起,我无法发表评论,因为我没有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和processessOrders的静态变量作为控制线程何时停止运行的方式似乎很令人担忧,因为我认为每个线程都有自己的副本,因此它也可能导致竞争条件。我可能错了。我不确定你还没看到什么,但我认为this has some good information that might help

答案 1 :(得分:1)

Worker等待订单时,实际上会等待多个事件之一

  1. buffer变为非空。
  2. 所有Tills完成了他们的工作,因此不会生成任何订单。
  3. 最好将所有这些事件组合成单一保护(在您的情况下为Semaphore)。并将等待实现移至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 。这种一次性等待可以使用最初锁定的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以供Worker调用。

    因为worker只从缓冲区获取 order ,所以此对象(类型为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 运行方法的所有内容。请注意,现在这种方法没有直接使用任何同步,一切都在低级别的类中完成:

    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);
                }
            }
        }
    }
    
相关问题