[英]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.