簡體   English   中英

用Java執行隊列中的線程

[英]Execution of threads in a queue in Java

我有這個練習:

開發多線程應用程序。 使用java.util.concurrent機會。

不使用:同步,BlockingQueue,BlockingDeque

希望訪問資源的所有實體必須是線程。 利用OOP的機會。

我的任務是:

免費CashDesk。 快餐餐廳有幾個收銀處。 客戶在特定的收銀台排隊,但是如果隊列中的隊列減少或消失,客戶可能會轉到另一個收銀台。

這是我的解決方案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);
    }
}

客戶代碼:

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代碼:

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

跑步者代碼:

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

我對此有疑問。 那就是我運行應用程序后得到的

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

因此,您可以看到服務隊列受到干擾。

當為client3提供服務​​時,client6必須開始提供服務,但是client50必須這樣做。 並且當為client5提供服務時,client4必須開始提供服務,但是client7必須這樣做。 而且當client7被提供服務時,我不知道為什么,但是client6移至cashDesk#1並開始提供服務,盡管client4必須開始提供服務。

我是多線程新手,所以我需要一個建議,如何使我的應用程序正常工作

您在標題中討論了隊列,但未在代碼中使用它們。 實際上,當第一個客戶(client5)到達Cashdesk1時,Cashdesk被鎖定為該客戶提供服務。

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

同時,由於有服務時間,其他客戶也會來。 因此client4和client7在Cashdesk1上等待
服務client5后,client5釋放鎖定

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

因此,下一個要服務的對象是第一個獲取鎖定的對象,由於這是一個無限循環,因此您無法知道每個客戶端在代碼中的哪個位置。 因此,client7首先在client4之前獲取它。 此外,讀取您的輸出后,client2也會對其進行捕獲。
我建議您刪除鎖並使用變量指定順序

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

替換下面的代碼部分

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

通過

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

因此,您可以看到服務隊列受到干擾。

不打擾。 它實際上是由您設計的。

ReentrantLock文檔:

此類的構造函數接受一個可選的fairness參數。 設置為true時,在爭用條件下,鎖傾向於授予對等待時間最長的線程的訪問。 否則,此鎖不能保證任何特定的訪問順序。 與使用默認設置的程序相比,使用許多線程訪問的公平鎖的程序可能會顯示較低的總體吞吐量(即,速度較慢;通常要慢得多),但獲得鎖的時間變化較小,並確保沒有飢餓。

但是請注意,鎖的公平性不能保證線程調度的公平性。 因此,使用公平鎖的許多線程之一可能會連續多次獲得它,而其他活動線程沒有進行且當前未持有該鎖。

還要注意,未定時的tryLock方法不支持公平設置。 如果鎖定可用,即使其他線程正在等待,它將成功。

嘗試熟悉ReentrantLock的使用。 公平參數(您可以將其作為值傳遞給構造函數 )是第一步,看看您的客戶是否按照您希望的順序獲得服務。 兩種解決方案(另一個答案都建議使用變量來控制順序,盡管我不太喜歡這樣做-或我的建議是使用公平性參數集),然后通過更多反饋與我們聯系。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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