我有这个练习:
开发多线程应用程序。
使用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);
}
}
}