繁体   English   中英

Java线程项目怪异的行为,同时增加了线程数

[英]Java thread project weird behavior while increasing num of threads

我创建了一个用于研究目的的项目,该项目使用Threads模拟餐厅服务。 有一个线程供厨师准备饭菜,还有一个线程供服务员提供饭菜。 当我与1位厨师和5位侍者一起测试时,它工作正常。 但是,当我增加厨师人数时,该程序将无限期运行。 怎么了? 这是代码:

主班

package restaurant;

import java.util.concurrent.Semaphore;

public class Main {

    public static int MAX_NUM_MEALS = 5;
    public static int OLDEST_MEAL = 0;
    public static int NEWEST_MEAL = -1;
    public static int DONE_MEALS = 0;

    public static int NUM_OF_COOKS = 1;
    public static int NUM_OF_WAITERS = 5;

    public static Semaphore mutex = new Semaphore(1);

    static Cook cookThreads[] = new Cook[NUM_OF_COOKS];
    static Waiter waiterThreads[] = new Waiter[NUM_OF_WAITERS];

    public static void main(String[] args) {
        for(int i = 0; i < NUM_OF_COOKS; i++) {
            cookThreads[i] = new Cook(i);
            cookThreads[i].start();
        }

        for(int i = 0; i < NUM_OF_WAITERS; i++) {
            waiterThreads[i] = new Waiter(i);
            waiterThreads[i].start();
        }
        try {

            for(int i = 0; i < NUM_OF_COOKS; i++) {
                cookThreads[i].join();
            }

            for(int i = 0; i < NUM_OF_WAITERS; i++) {
                waiterThreads[i].join();
            }

        }catch(InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("All done");

    }


}

班级厨师

package restaurant;

public class Cook extends Thread{

    private int id;

    public Cook(int id) {
        this.id = id;
    }

    public void run() {
        while(true) {
            System.out.println("Cook " + id + " is prepearing meal");
            try {
                Thread.sleep(1000);

                Main.mutex.acquire();
                Main.NEWEST_MEAL++;
                Main.mutex.release();

                Main.mutex.acquire();
                Main.DONE_MEALS++;
                Main.mutex.release();

                System.out.println("Cook " + id + " has finished the meal");

                if(Main.DONE_MEALS == 5) {
                    System.out.println("Cook " + id + " has finished his job");
                    break;
                }

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

班级服务员

package restaurant;

public class Waiter extends Thread{
    private int id;

    public Waiter(int id) {
        this.id = id;
    }

    public void run() {
        while(true) {
            System.out.println("Waiter " + id + " will check if there is any meal to serve");
            if(Main.NEWEST_MEAL >= Main.OLDEST_MEAL) {
                try {
                    Main.mutex.acquire();
                    Main.OLDEST_MEAL++;
                    Main.mutex.release();

                    System.out.println("Waiter " + id + " is picking up meal");

                    Thread.sleep(500);

                    System.out.println("Waiter " + id + " has delivered the meal to client");                   

                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(Main.DONE_MEALS == 5) {
                System.out.println("Waiter " + id + " has finished his job");
                break;
            }
            System.out.println("No meal to serve. Waiter " + id + " will come back later");

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

两个问题:

  1. 因为您有两名厨师,所以您的一名厨师可能看不到Main.DONE_MEALS == 5 由于有其他厨师,它将从4跳到6。 相反,请检查Main.DONE_MEALS >= 5
  2. 无法保证厨师线程或服务员线程将看到Main.DONE_MEALS的更新。 相反,请考虑使用private static final AtomicInteger字段。 AtomicInteger类是一个线程安全的整数实现,使其他线程能够以线程安全的方式查看它。

传统的解决方法是:

a)您不仅必须在写时使用锁(互斥锁),而且在阅读时也必须使用锁-否则它将无法正常工作。 试想一下,如果您同意一个信号以指示浴室是否忙碌,但有些人只是决定忽略它-则行不通!

b)在做某事之前,请检查条件。
一旦获得了锁,您就不会知道状态,因此您应该先检查一下它,然后再做一顿饭。 如果您首先检查是否已经做过5顿饭,而仅在没有5 done_meals <= 5 ,那应该可以解决此问题,并且您应该只看到done_meals <= 5 (您应该查看代码的其他部分,因为它有类似的问题)。

就像其他人提到的那样,有一些更干净的方法可以编写此代码,但是IMO您的代码非常适合实践和理解,因此我会尝试这样做,而不是像AtomicInteger这样跳槽。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM