Tuesday, August 5, 2014

Java Concurrency : ClientServer Simulation

Thinking in Java Ex35 [p1259]

Q. Modify BankTellerSimulation.java so that it represents Web clients making requests of a fixed number of servers. The goal is to determine the load that the group of servers can handle.

My solution:

import java.util.concurrent.*;
import java.util.*;

//Read only objects don't require synchronization
class Client {

    private final int serviceTime;

    public Client(int tm) {
        serviceTime = tm;
    }

    public int getServiceTime() {
        return serviceTime;
    }

    public String toString() {
        return "[" + serviceTime + "]";
    }
}

//Teach the customer line to display itself:
class ClientLine extends ArrayBlockingQueue<Client> {

    public ClientLine(int maxLineSize) {
        super(maxLineSize);
    }

    public String toString() {
        if (this.size() == 0) {
            return "[Empty]";
        }
        StringBuilder result = new StringBuilder();
        for (Client customer : this) {
            result.append(customer);
        }
        return result.toString();
    }
}

//Randomly add customers to a queue:
class ClientGenerator implements Runnable {

    private ClientLine customers;
    volatile int loadFactor = 1; //Start with one client/sec
    private static Random rand = new Random(47);

    public ClientGenerator(ClientLine cq) {
        customers = cq;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(rand.nextInt(100));
                customers.put(new Client(rand.nextInt(1000)));
            }
        } catch (InterruptedException e) {
            System.out.println("ClientGenerator interrupted");
        }
        System.out.println("ClientGenerator terminating");
    }
}

class Server implements Runnable, Comparable<Server> {

    private static int counter = 0;
    private final int id = counter++;
    //Clients served during this shift:
    private int customersServed = 0;
    private ClientLine customers;
    private boolean servingClientLine = true;

    public Server(ClientLine cq) {
        customers = cq;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                Client customer = customers.take();
                TimeUnit.MILLISECONDS.sleep(
                        customer.getServiceTime());
                synchronized (this) {
                    customersServed++;
                    while (!servingClientLine) {
                        wait();
                    }
                }
            }
        } catch (InterruptedException e) {
            System.out.println(this + "interrupted");
        }
        System.out.println(this + "terminating");
    }

    public synchronized void doSomethingElse() {
        customersServed = 0;
        servingClientLine = true;
        notifyAll();
    }

    public synchronized void serveClientLine() {
        assert !servingClientLine : "already serving: " + this;
        servingClientLine = true;
        notifyAll();
    }

    public String toString() {
        return "Server " + id + " ";
    }

    public String shortString() {
        return "T" + id;
    }
    // Used by priority queue:

    public synchronized int compareTo(Server other) {
        return customersServed < other.customersServed ? -1
                : (customersServed == other.customersServed ? 0 : 1);
    }
}

class ServerManager implements Runnable {

    static int LIMIT = 0;
    private ExecutorService exec;
    private ClientLine customers;
    private PriorityQueue<Server> workingServers
            = new PriorityQueue<Server>();
    private Queue<Server> tellersDoingOtherThings
            = new LinkedList<Server>();
    private int adjustmentPeriod;
    private static Random rand = new Random(47);

    public ServerManager(ExecutorService e, ClientLine customers, int adjustmentPeriod) {
        exec = e;
        this.customers = customers;
        this.adjustmentPeriod = adjustmentPeriod;
// Start with a single teller:
        Server teller = new Server(customers);
        exec.execute(teller);
        workingServers.add(teller);
    }

    public void adjustServerNumber() {
    // This is actually a control system. By adjusting
    // the numbers, you can reveal stability issues in
    // the control mechanism.
    // If line is too long, add another teller:
        if (customers.size() / workingServers.size() > 2) {
    // If tellers are on break or doing
    // another job, bring one back:
            if (tellersDoingOtherThings.size() > 0) {
                Server teller = tellersDoingOtherThings.remove();
                teller.serveClientLine();
                workingServers.offer(teller);
                return;
            }
    // Else create (hire) a new teller     
            if(LIMIT<5){
            Server teller = new Server(customers);
            exec.execute(teller);
            workingServers.add(teller);
            LIMIT++;
            return;
            }
            System.out.println("Max Clients supported: "+customers.size());
            System.exit(0);
            return;
        }
    // If line is short enough, remove a teller:
        if (workingServers.size() > 1
                && customers.size() / workingServers.size() < 2) {
            reassignOneServer();
        }
    // If there is no line, we only need one teller:
        if (customers.size() == 0) {
            while (workingServers.size() > 1) {
                reassignOneServer();
            }
        }
    }
    // Give a teller a different job or a break:

    private void reassignOneServer() {
        Server teller = workingServers.poll();
        teller.doSomethingElse();
        tellersDoingOtherThings.offer(teller);
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(adjustmentPeriod);
                adjustServerNumber();
                System.out.print(customers + " { ");
                for (Server teller : workingServers) {
                    System.out.print(teller.shortString() + " ");
                }
                System.out.println("}");
            }
        } catch (InterruptedException e) {
            System.out.println(this + "interrupted");
        }
        System.out.println(this + "terminating");
    }

    public String toString() {
        return "ServerManager ";
    }
}

public class ServerSimulation {

    static final int MAX_LINE_SIZE = 1000;
    static final int ADJUSTMENT_PERIOD = 1000;

    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        // If line is too long, customers will leave:
        ClientLine customers
                = new ClientLine(MAX_LINE_SIZE);
        exec.execute(new ClientGenerator(customers));
        // Manager will add and remove tellers as necessary:
        exec.execute(new ServerManager(
                exec, customers, ADJUSTMENT_PERIOD));
        if (args.length > 0) // Optional argument
        {
            TimeUnit.SECONDS.sleep(new Integer(args[0]));
        } else {
            System.out.println("Press Enter to quit");
            System.in.read();
        }
        exec.shutdownNow();
    }
}

Output:

Press ‘Enter’ to quit
[200][207][128][589][278][861][258][140][322][383][575][342][804][826][896][984][810][141][12][689] { T1 T0 }
[861][258][140][322][383][575][342][804][826][896][984][810][141][12][689][992][976][368][395][354][222][687][634][317][242][698][899][665][909][209][158][273][894][984][533][8][328] { T2 T0 T1 }
[826][896][984][810][141][12][689][992][976][368][395][354][222][687][634][317][242][698][899][665][909][209][158][273][894][984][533][8][328][779][882][37][871][17][828][696][994][419][738][434][106][718][965][416][217][677][146][552] { T3 T2 T1 T0 }
[992][976][368][395][354][222][687][634][317][242][698][899][665][909][209][158][273][894][984][533][8][328][779][882][37][871][17][828][696][994][419][738][434][106][718][965][416][217][677][146][552][244][111][934][843][585][966][615][913][921][213][409][983][158][225][326][851][198][505][332][310][933] { T4 T3 T1 T0 T2 }
[698][899][665][909][209][158][273][894][984][533][8][328][779][882][37][871][17][828][696][994][419][738][434][106][718][965][416][217][677][146][552][244][111][934][843][585][966][615][913][921][213][409][983][158][225][326][851][198][505][332][310][933][110][371][529][830][883][236][824][907][381][458][313][688][883][996][813][147][790][265][846][990][122] { T5 T3 T4 T0 T2 T1 }
Max Clients supported: 80

No comments:

Post a Comment