简体   繁体   English

用Java执行队列中的线程

[英]Execution of threads in a queue in Java

I have this exercise: 我有这个练习:

Develop a multi-threaded application. 开发多线程应用程序。 Use java.util.concurrent opportunities. 使用java.util.concurrent机会。

DONT USE: synchronized, BlockingQueue, BlockingDeque 不使用:同步,BlockingQueue,BlockingDeque

All entities wishing to access a resource must be threads. 希望访问资源的所有实体必须是线程。 Use the opportunities of OOP. 利用OOP的机会。

And my task is: 我的任务是:

Free cashDesk. 免费CashDesk。 Fast food restaurant has a few cash Desks. 快餐餐厅有几个收银处。 Client stand in queue at a particular cash Desk, but may go to another cash Desk if reduction or disappearance of the queue there. 客户在特定的收银台排队,但是如果队列中的队列减少或消失,客户可能会转到另一个收银台。

Here is my solution https://github.com/NikitaMitroshin/FreeCash 这是我的解决方案https://github.com/NikitaMitroshin/FreeCash

public class Restaurant {
    private static Restaurant instance = null;
    private static ReentrantLock lock = new ReentrantLock();
    private String name;
    private ArrayList<CashDesk> cashDesks;

    private Restaurant(String name) {
        this.name = name;
        cashDesks = new ArrayList<>();
    }

    public static Restaurant getInstance(String name) {
        lock.lock();
        try {
            if (instance == null) {
                instance = new Restaurant(name);
            }
        } finally {
            lock.unlock();
        }
        return instance;
    }

    public void addCashDesk(CashDesk cashDesk) {
        cashDesks.add(cashDesk);
    }

    public String getName() {
        return name;
    }

    public List<CashDesk> getCashDesks() {
        return Collections.unmodifiableList(cashDesks);
    }
}

Client code: 客户代码:

public class Client extends Thread {
    private final static Logger LOG = Logger.getLogger(Client.class);
    private Restaurant restaurant;
    private CashDesk cashDesk;
    private String name;
    private int itemsInOrder;

    public Client(Restaurant restaurant, int itemsInOrder, String name) {
        this.restaurant = restaurant;
        this.itemsInOrder = itemsInOrder;
        this.name = name;
    }

    public String getClientName() {
        return name;
    }

    public int getItemsInOrder() {
        return itemsInOrder;
    }

    @Override
    public void run() {
        System.out.println("Client " + name + " comes to restaurant " + restaurant.getName());
        this.cashDesk = chooseCashDesk();
        System.out.println("Client " + getClientName() + " choosed the cashDesk#"+ cashDesk.getNumber());
        cashDesk.addClient(this);
        while (true) {
            if (cashDesk.getLock().tryLock()) {
                try {
                    cashDesk.serveClient(this);
                } catch (ResourceException e) {
                    LOG.error("ResourceException!!! ", e);
                } finally {
                    cashDesk.getLock().unlock();
                    break;
                }
            } else {
                if (canChooseAnotherCashDesk()) {
                    cashDesk.removeClient(this);
                }
            }
        }
        cashDesk.removeClient(this);
        System.out.println("Client " + getClientName() + " leaves restaurant");
    }

    private CashDesk chooseCashDesk(){
        CashDesk result = restaurant.getCashDesks().get(0);
        for (CashDesk cashDesk : restaurant.getCashDesks()) {
            if(cashDesk.getClients().size() < result.getClients().size()) {
                result = cashDesk;
            }
        }
        return result;
    }

    private boolean canChooseAnotherCashDesk() {
        CashDesk result = chooseCashDesk();
        if(result.getClients().size() + 1 < cashDesk.getClients().size()) {
            cashDesk = result;
            cashDesk.addClient(this);
            System.out.println("Client " + getClientName() + " moved to cashDesk#" + cashDesk.getNumber());
            return true;
        }
        return false;
    }
}

CashDesk code: CashDesk代码:

public class CashDesk {

    private ReentrantLock lock = new ReentrantLock();
    private LinkedList<Client> clients;
    private int number;
    private int timeOfService;

    public CashDesk(int number, int timeOfService) {
        clients = new LinkedList<>();
        this.number = number;
        this.timeOfService = timeOfService;

    }

    public void serveClient(Client client) throws ResourceException {
        System.out.println("Client "+client.getClientName() + " is serving on cashDesk#"+getNumber());
        try {
            client.sleep(timeOfService * client.getItemsInOrder());
        } catch (InterruptedException e) {
            throw new ResourceException("InterruptedException!!!", e);
        }
        System.out.println("Client "+client.getClientName() + " is served");
    }

    public List<Client> getClients() {
        return Collections.unmodifiableList(clients);
    }

    public void addClient(Client client) {
        clients.add(client);
    }

    public void removeClient(Client client) {
        clients.remove(client);
    }

    public int getNumber() {
        return number;
    }

    public ReentrantLock getLock() {
        return lock;
    }
}

Runner code: 跑步者代码:

public class RestaurantRunner {

    public static void main(String[] args) {
        Restaurant restaurant = Restaurant.getInstance("Mcdonalds");
        CashDesk cashDesk1 = new CashDesk(1, 140);
        CashDesk cashDesk2 = new CashDesk(2, 250);

        restaurant.addCashDesk(cashDesk1);
        restaurant.addCashDesk(cashDesk2);


        new Client(restaurant, 100, "client50").start();
        Random random = new Random();
        for (int i = 1; i < 8; i++) {

            int randNumbOfItems = random.nextInt(10) + 1;
            Client client =  new Client(restaurant, randNumbOfItems, "client"+i);
            client.start();
        }
    }
}

And I have problem with it. 我对此有疑问。 That's what I get after running my application 那就是我运行应用程序后得到的

Client client1 comes to restaurant Mcdonalds
Client client1 choosed the cashDesk#1
Client client1 is serving on cashDesk#1
Client client3 comes to restaurant Mcdonalds
Client client3 choosed the cashDesk#2
Client client3 is serving on cashDesk#2
Client client5 comes to restaurant Mcdonalds
Client client5 choosed the cashDesk#1
Client client6 comes to restaurant Mcdonalds
Client client6 choosed the cashDesk#2
Client client4 comes to restaurant Mcdonalds
Client client4 choosed the cashDesk#1
Client client50 comes to restaurant Mcdonalds
Client client50 choosed the cashDesk#2
Client client7 comes to restaurant Mcdonalds
Client client7 choosed the cashDesk#1
Client client2 comes to restaurant Mcdonalds
Client client2 choosed the cashDesk#2
Client client1 is served
Client client5 is serving on cashDesk#1
Client client1 leaves restaurant
Client client3 is served
Client client3 leaves restaurant
Client client50 is serving on cashDesk#2
Client client5 is served
Client client5 leaves restaurant
Client client7 is serving on cashDesk#1
Client client7 is served
Client client7 leaves restaurant
Client client6 moved to cashDesk#1
Client client6 is serving on cashDesk#1
Client client2 moved to cashDesk#1
Client client6 is served
Client client6 leaves restaurant
Client client2 is serving on cashDesk#1
Client client2 is served
Client client2 leaves restaurant
Client client4 is serving on cashDesk#1
Client client4 is served
Client client4 leaves restaurant
Client client50 is served
Client client50 leaves restaurant

So, as you can see queue of service is disturbed. 因此,您可以看到服务队列受到干扰。

When client3 is served client6 must start serving, but client50 doing this. 当为client3提供服务​​时,client6必须开始提供服务,但是client50必须这样做。 And when client5 is served client4 must start serving, but client7 doing this. 并且当为client5提供服务时,client4必须开始提供服务,但是client7必须这样做。 And when client7 is served I dont know why but client6 moving to cashDesk#1 and starts serving, despite the fact that client4 must start serving. 而且当client7被提供服务时,我不知道为什么,但是client6移至cashDesk#1并开始提供服务,尽管client4必须开始提供服务。

I'm novice at multithreading so I need an advice, how to get my application work correctly 我是多线程新手,所以我需要一个建议,如何使我的应用程序正常工作

You talked about queues in the title but you didn't use them in your code. 您在标题中讨论了队列,但未在代码中使用它们。 In fact when the first client (client5) comes at the cashdesk1, the cashdesk is locked to serve this client. 实际上,当第一个客户(client5)到达Cashdesk1时,Cashdesk被锁定为该客户提供服务。

  //client code
          while (true) {
        if (cashDesk.getLock().tryLock()) {  //the cashdesk is locked
            try {
                cashDesk.serveClient(this);

Meanwhile other clients come since there is a service time. 同时,由于有服务时间,其他客户也会来。 So client4 and client7 are waiting at the cashdesk1 因此client4和client7在Cashdesk1上等待
When client5 is served, client5 releases the lock 服务client5后,client5释放锁定

  //client code
   cashDesk.getLock().unlock();

So the next one to be served is the first to grab the lock and since it is an infinite loop you cannot know at which position in your code each client is. 因此,下一个要服务的对象是第一个获取锁定的对象,由于这是一个无限循环,因此您无法知道每个客户端在代码中的哪个位置。 So client7 grabs it first before client4. 因此,client7首先在client4之前获取它。 Moreover reading your output, client2 grabs it before too. 此外,读取您的输出后,client2也会对其进行捕获。
I suggest you remove the lock and use a variable to specify the order 我建议您删除锁并使用变量指定顺序

 //CashDesk
  Client current=null;
  public void nextClient() 
  {
    if(clients.size()==0)
        current=null;
    else
        current = clients.get(0);
 } 

Replace the portion following code 替换下面的代码部分

 while (true) {
        if (cashDesk.getLock().tryLock()) {
            try {
                cashDesk.serveClient(this);
            } catch (ResourceException e) {
                LOG.error("ResourceException!!! ", e);
            } finally {
                cashDesk.getLock().unlock();
                break;
            }
        } else {
            if (canChooseAnotherCashDesk()) {
                cashDesk.removeClient(this);
            }
        }
    }

by 通过

  while (true) {
        if(cashDesk.current==null)
             cashDesk.nextClient();
        if (current==this) {
            try {
                cashDesk.serveClient(this);
            } catch (ResourceException e) {
                LOG.error("ResourceException!!! ", e);
            } finally {
                cashDesk.nextClient();
                break;
            }
        } else {
            if (canChooseAnotherCashDesk()) {
                cashDesk.removeClient(this);
            }
        }
    }

So, as you can see queue of service is disturbed. 因此,您可以看到服务队列受到干扰。

It is not disturbed. 不打扰。 It works as designed by you actually. 它实际上是由您设计的。

ReentrantLock documentation: ReentrantLock文档:

The constructor for this class accepts an optional fairness parameter. 此类的构造函数接受一个可选的fairness参数。 When set true, under contention, locks favor granting access to the longest-waiting thread. 设置为true时,在争用条件下,锁倾向于授予对等待时间最长的线程的访问。 Otherwise this lock does not guarantee any particular access order. 否则,此锁不能保证任何特定的访问顺序。 Programs using fair locks accessed by many threads may display lower overall throughput (ie, are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. 与使用默认设置的程序相比,使用许多线程访问的公平锁的程序可能会显示较低的总体吞吐量(即,速度较慢;通常要慢得多),但获得锁的时间变化较小,并确保没有饥饿。

Note however, that fairness of locks does not guarantee fairness of thread scheduling. 但是请注意,锁的公平性不能保证线程调度的公平性。 Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock. 因此,使用公平锁的许多线程之一可能会连续多次获得它,而其他活动线程没有进行且当前未持有该锁。

Also note that the untimed tryLock method does not honor the fairness setting. 还要注意,未定时的tryLock方法不支持公平设置。 It will succeed if the lock is available even if other threads are waiting. 如果锁定可用,即使其他线程正在等待,它将成功。

Try to get acquainted with the use of ReentrantLock . 尝试熟悉ReentrantLock的使用。 The fairness parameter (you can pass it as a value to the constructor ) is a first step to see if your clients get served in the order you'd like them to. 公平参数(您可以将其作为值传递给构造函数 )是第一步,看看您的客户是否按照您希望的顺序获得服务。 The both solutions (the other answer suggests to use a variable to control order, though I'm not a big fan of that - or my suggestion with a fairness parameter set) and get back to us with more feedback. 两种解决方案(另一个答案都建议使用变量来控制顺序,尽管我不太喜欢这样做-或我的建议是使用公平性参数集),然后通过更多反馈与我们联系。

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

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