import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
public class Main {
private static final int CLIENTS_NUM = 100;
private static final int TELLERS_NUM = 3;
private static final int SAFE_TELLERS_ACCESS = 2;
private static final Semaphore safe = new Semaphore(SAFE_TELLERS_ACCESS);
private static final Queue clientLine = new ConcurrentLinkedQueue<>();
public static void main(String[] args) {
for (int i = 0; i < CLIENTS_NUM; i++) {
Client client = new Client(i);
client.start();
}
List tellers = new ArrayList<>();
for (int i = 0; i < TELLERS_NUM; i++) {
Teller teller = new Teller(i);
tellers.add(teller);
teller.start();
}
try {
for (Teller teller : tellers) {
teller.join();
}
} catch (InterruptedException exception) {
exception.printStackTrace();
}
System.out.println("Bank closes");
}
static class Client extends Thread {
private final int clientId;
private ClientOperation clientOperation;
Client(int clientId) {
this.clientId = clientId;
}
public int getClientId() {
return clientId;
}
public ClientOperation getClientOperation() {
return clientOperation;
}
public synchronized void setTeller(Teller teller) {
System.out.println(MessageFormat.format("Client {0} goes to Teller {1}", clientId, teller.getTellerId()));
notify();
}
public synchronized void startProcessing() {
System.out.println(MessageFormat.format("Client {0} wants to make a {1}", clientId, clientOperation));
notify();
}
public synchronized void finishProcessing() {
System.out.println(MessageFormat.format("Client {0} is done", clientId));
notify();
}
public synchronized void finishServing() {
System.out.println(MessageFormat.format("Client {0} leaves", clientId));
notify();
}
@Override
public synchronized void run() {
try {
if (new Random().nextBoolean()) {
clientOperation = ClientOperation.Deposit;
} else {
clientOperation = ClientOperation.Withdraw;
}
clientLine.add(this);
System.out.println(MessageFormat.format("Client {0} waits in line to make a {1}", clientId, clientOperation));
// waiting for some teller to start servicing
wait();
// waiting for teller to start processing
wait();
// waiting for teller to finish processing
wait();
// wainting for teller to finish servicing
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
enum ClientOperation {
Deposit,
Withdraw
}
static class Teller extends Thread {
private final int tellerId;
Teller(int tellerId) {
this.tellerId = tellerId;
}
public int getTellerId() {
return tellerId;
}
@Override
public void run() {
System.out.println(MessageFormat.format("Teller {0} is available", tellerId));
Client client;
while ((client = clientLine.poll()) != null) {
try {
client.setTeller(this);
int clientId = client.getClientId();
System.out.println(MessageFormat.format("Teller {0} is serving Client {1}", tellerId, clientId));
client.startProcessing();
ClientOperation clientOperation = client.getClientOperation();
System.out.println(MessageFormat.format("Teller {0} is processing Client {1}''s {2}", tellerId, clientId, clientOperation));
if (clientOperation == ClientOperation.Withdraw) {
System.out.println(MessageFormat.format("Teller {0} is getting the manager's approval", tellerId));
long managersApprovalTime = new Random().nextInt(251) + 50;
Thread.sleep(managersApprovalTime);
System.out.println(MessageFormat.format("Teller {0} is got the manager's approval", tellerId));
}
long useSafeTime = new Random().nextInt(401) + 100;
System.out.println(MessageFormat.format("Teller {0} is going to the safe", tellerId));
Thread.sleep(useSafeTime);
safe.acquire();
System.out.println(MessageFormat.format("Teller {0} is using the safe", tellerId));
safe.release();
System.out.println(MessageFormat.format("Teller {0} is done using the safe", tellerId));
System.out.println(MessageFormat.format("Teller {0} notifies Client {1}", tellerId, clientId));
client.finishProcessing();
System.out.println(MessageFormat.format("Teller {0} has finished serving Client {1}", tellerId, clientId));
client.finishServing();
System.out.println(MessageFormat.format("Teller {0} is available", tellerId));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(MessageFormat.format("Teller {0} Closes", tellerId));
}
}
}