我有这个练习:
开发一个多线程应用程序。 使用java.util.concurrent机会。
不要使用:synchronized,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必须开始服务。
我是多线程的新手所以我需要一个建议,如何让我的应用程序正常工作
答案 0 :(得分:3)
您谈到了标题中的队列,但您没有在代码中使用它们。事实上,当第一个客户(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);
}
}
}
答案 1 :(得分:0)
因此,您可以看到服务队列受到干扰。
没有受到干扰。它实际上是按照你的设计工作的。
ReentrantLock文档:
此类的构造函数接受可选的fairness参数。 当设置为true时,在争用下,锁有利于授予访问权限 最长等待的线程。 否则此锁不保证任何 特定访问顺序。使用公平锁的程序 线程可以显示较低的总吞吐量(即,较慢;通常 比使用默认设置的那些慢得多,但要小一些 获得锁定的时间差异和保证缺乏饥饿。
但请注意,锁的公平性并不能保证公平性 线程调度。因此,使用公平锁定的许多线程之一可以 在其他活动线程连续多次连续获取它 没有进展,目前没有锁定。
另请注意 不定时的tryLock方法不尊重公平性设置。它会 如果锁定可用,即使其他线程正在等待,也会成功。
尝试熟悉ReentrantLock
的使用。公平性参数(您可以将其作为值传递给constructor)是了解您的客户是否按照您希望的顺序投放的第一步。
这两个解决方案(另一个答案建议使用一个变量来控制顺序,虽然我不是那个的忠实粉丝 - 或者我的公平参数设置的建议)并回复给我们更多的反馈。