如何在java中同步线程

时间:2016-09-11 04:13:24

标签: java thread-safety

我需要注册每个客户的订单 服务员只能在写下客户的所有请求后才能下达订单。 客户必须等到请求交付

我无法将客户端操作与服务员同步

我需要从我的对象Customer和Waiter同步每个动作 客户有1个订单 服务员有N个客户

要同步的动作序列在每个线程的“run”方法中实现

package room;

import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Customer extends Thread {

    private Random random;
    private int id;
    private static int ID;
    private Order order;
    private Waiter waiter;

    public Customer(Waiter garcom) {
        this.random = new Random();
        this.id = ID + 1;
        Customer.ID++;
        this.waiter = garcom;
    }

    @Override
    public void run() {
        orderRequest();
        waitOrder();
        receiveRequest();
        consumer();
    }

    public synchronized void orderRequest() {
        synchronized (this.order) {

            int r = random.nextInt(3);
            switch (r) {
                case 0:
                    this.order.setOrder("Food");
                    break;
                case 1:
                    this.order.setOrder("Drink");
                    break;
                default:
                    this.order.setOrder("Help");
                    break;
            }

            System.out.println(this.toString() + " request " + this.order.getOrder() + "to " + this.waiter.toString());

              this.order.notify();

            try {
                this.order.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Customer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    public synchronized void receiveRequest() {

        synchronized (this.order) {

            this.order.notify();

            try {
                this.order.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Customer.class.getName()).log(Level.SEVERE, null, ex);
            }

            System.out.println(this + " has received " + this.order + " from " + this.waiter);
        }
    }

    private void waitOrder() {
        synchronized (this.order) {

            System.out.println(this + " consumer " + this.order);

            this.order.notify();

            try {
                this.order.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Customer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    public synchronized void consumer() {

        synchronized (this.order) {

            System.out.println(this + " was consumed " + this.order);

            this.order.notify();

            try {
                this.order.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(Customer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    public void setOrder(Order order) {
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }

    @Override
    public String toString() {
        return "Customer: " + id;
    }
}

package room;

public class Order {

    private int orderNumber;
    private static int ORDER_NUMBER = 0;
    private Customer customer;
    private String order;

    public Order(Customer c) {
        this.customer = c;
        this.orderNumber = ORDER_NUMBER + 1;
        this.customer.setOrder(this);
        Order.ORDER_NUMBER++;

    }

    public String getOrder() {
        return order;
    }

    public int getOrderNumber() {
        return orderNumber;
    }


    public void setOrder(String order) {
        this.order = order;
    }

    public Customer getCustomer() {
        return customer;
    }

    @Override
    public String toString() {
        return "Order: " + order + " Nº " + orderNumber;
    }

}

package room;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Waiter extends Thread {

    private Saloon saloon;
    private int id;
    private static int ID = 0;
    private List<Order> orders;

    public Waiter(Saloon bar) {
        this.saloon = bar;
        this.id = ID + 1;
        Waiter.ID++;
        this.orders = new ArrayList<>();

    }

    @Override
    public void run() {

        while (saloon.isOpen()) {

            registerOrder();
            deliveryRequest();

            saloon.round();
        }
    }

    public synchronized void registerOrder() {

        for (Order order : orders) {

            synchronized (order) {

                order.notify();

                try {
                    order.wait();
                } catch (InterruptedException e) {
                    System.out.println(e.getMessage());
                }
                System.out.println(this.toString() + " "
                        + "record " + order.toString()
                        + " to " + order.getCustomer());

            }
        }

    }

    public synchronized void deliveryRequest() {

        for (Order order : orders) {

            synchronized (order) {

                order.notify();

                try {
                    order.wait();
                } catch (InterruptedException ex) {
                    Logger.getLogger(Waiter.class.getName()).log(Level.SEVERE, null, ex);
                }

                System.out.println(this.toString() + " "
                        + "delivered " + order.toString()
                        + " to " + order.getCustomer());

            }
        }
    }

    public synchronized void recordOrder(Order order) {

        synchronized (orders) {
            this.orders.add(order);
        }
    }

    public List<Order> getOrders() {
        return orders;
    }

    public Saloon getSaloon() {
        return saloon;
    }

    @Override
    public String toString() {
        return "Waiter: " + id;
    }
}

package room;

import java.util.ArrayList;
import java.util.List;

public class Saloon {

    private int maxRound;
    private int numGarcons;
    private volatile int round;
    private int capacity;
    private int customerCount;
    private final List<Waiter> waiters;

    public Saloon() {
        this.waiters = new ArrayList<>();
        this.round = 1;
    }

    public Saloon(int customerCount, int waiterCount,
            int capacity, int rounds) {
        this();

        this.numGarcons = waiterCount;
        this.customerCount = customerCount;
        this.capacity = capacity;
        this.maxRound = rounds;

    }

    /**
     *  Should it be call each round
     */
    public void openBar() {

        this.capacity = this.customerCount / this.capacity;
        System.out.println("Round " + this.round);

        for (int i = 0; i < this.numGarcons; i++) {

            //Create a waiter
            Waiter g = new Waiter(this);

            for (int j = 0; j < this.capacity; j++) {
                //create customer
                Customer c = new Customer(g);
                //an order
                Order p = new Order(c);
                //register order
                g.recordOrder(p);
                //call thread client
                c.start();
            }
            //waiter serves one client at a time
            g.start();

            this.waiters.add(g);
        }

    }

    public boolean isOpen() {

        if (this.round < this.maxRound) {
            return true;
        }
        return false;
    }

    public void round() {
        this.round++;
    }
}

1 个答案:

答案 0 :(得分:0)

好吧,我必须说我很享受挑战......但我已经想到了这一点。我应该注意到我尝试了你的原始maxRounds,虽然它有效但有一个额外的客户可以通过队列的错误。 注意:我并不是说这是绝对做到这一点的最佳方式......也不一定是最优雅的,但它应该指向正确的方向。

我建议阅读BlockingQueues和扩展它的类。

主要打电话给沙龙:

public static void main(String[] args) {
  Saloon saloon = new Saloon(100, 4, 50);
  saloon.openBar();
}

Saloon Class:

package room;

import java.util.ArrayList;
import java.util.List;

public class Saloon {

    private volatile static int round;
    private static int maxRound;
    private int numWaiters;
    private final List<Waiter> waiters;
    private int customerMax; // current implementation doesn't need this but you could if you wanted
    private volatile static boolean isOpen;
    private OrderQueue<Order> orderQueue; // This is the creation of the queue

    public Saloon() {
        this.waiters = new ArrayList<>();
        Saloon.round = 1;
    }

    public Saloon(int customerMax, int numWaiters, int maxRounds) {
        this();
        this.customerMax = customerMax;
        this.numWaiters = numWaiters;
        this.orderQueue = new OrderQueue<Order>(numWaiters);
        Saloon.maxRound = maxRounds;
    }

    /**
     *  Should it be call each round
     */
    public void openBar() {
        System.out.println("Round " + round);
        isOpen = true;
        // Create all waiters at once outside the loop
        for (int i = 0; i < this.numWaiters; i++) {
            waiters.add(new Waiter(i + 1, orderQueue));
            waiters.get(i).start();
        }
        int customersServed = 1; // Generating the id number here is a better choice IMO
        while(isOpen) {
            for (Waiter waiter : waiters) {
                if (round >= maxRound) {
                    closeBar();
                }
                if (waiter.isAvailable() && !waiter.isClockedOut()) {
                    // create customer
                    Customer customer = new Customer(customersServed++, waiter, orderQueue);
                    customer.start();
                }
            }
        }
    }

    /**
     * A simple implementation to close the bar
     */
    public void closeBar() {
        isOpen = false; // ends the loop
        int waitersFinished = 0; // used to check if all waiters stopped serving
        while (waitersFinished < numWaiters) {
            for (Waiter waiter : waiters) {
                if (waiter.isAvailable()) {
                    synchronized (orderQueue) {
                        // if nothing is currently in the queue for the waiters
                        if (orderQueue.isEmpty()) {
                            orderQueue.done(); // close the queue
                            orderQueue.notify(); // notify the queue
                        }
                        waiter.clockOut(); // clockout the waiter could use better implementation
                        waitersFinished++; // increment waiters finished
                    }
                }
            }
        }
    }

    public static boolean isOpen() {
        return isOpen;
    }

    // I would not recommend using this... this is the source of the glitch
    public synchronized static void round() {
        if (round <= maxRound) { // only if reached maxRounds
            System.out.println("Round " + round);
            round++;
        }
    }
}

OrderQueue类:

package room;

import java.util.concurrent.ArrayBlockingQueue;

/**
 * 
 * Note I did not create this.. tomasb did and I implemented it.
 *
 * @param <T>
 */
public class OrderQueue<T> extends ArrayBlockingQueue<T> {

    private static final long serialVersionUID = 1L;
    private boolean done = false;

    /**
     * creates an order queue with a max capacity.. orders that can be concurrently handled
     * @param capacity
     */
    public OrderQueue(int capacity) {
        super(capacity);
    }

    /**
     * closes the while loop ending this orderqueue
     */
    public void done() {
        done = true;
    }

    public boolean isDone() {
        return done;
    }

    /**
     * May return null if producer ends the production after consumer has
     * entered the element-await state.
     */
    public T take() throws InterruptedException {
        T el;
        while ((el = super.poll()) == null && !done) {
            synchronized (this) {
                wait();
            }
        }

        return el;
    }
}

订单类:

package room;

public class Order {

    private int orderNumber;
    private static int ORDER_NUMBER = 0;
    private String order;

    public Order(int orderNumber) {
        this.orderNumber = ORDER_NUMBER + 1;
        this.orderNumber = orderNumber;
    }

    public String getOrder() {
        return order;
    }

    public int getOrderNumber() {
        return orderNumber;
    }

    public void setOrder(String order) {
        this.order = order;
    }

    @Override
    public String toString() {
        return "Order: " + order + " Nº " + orderNumber;
    }
}

客户类:

package room;

import java.util.Random;

public class Customer extends Thread {

    private int id;
    private int orderNumber = 1;
    private Order order;
    private Waiter waiter;
    private OrderQueue<Order> orderQueue; // This provides an esay way to pass objects

    public Customer(int id, Waiter waiter, OrderQueue<Order> orderQueue) {
        this.id = id;
        this.waiter = waiter;
        this.orderQueue = orderQueue;
        waiter.addCustomer(this);
        System.out.println("Customer: " + id + " sat at a table!");
    }

    @Override
    public void run() {
        // Notice that wait has been removed. The orderQueue takes care of this.
        orderRequest();
        receiveRequest();
        consumer();
        System.out.println("Customer: " + id + " has left satisfied!");
    }

    public void orderRequest() {
        // none of thie should be synchronized as it is local
        order = new Order(orderNumber++);

        Random random = new Random();
        int r = random.nextInt(3);
        switch (r) {
        case 0:
            order.setOrder("Food");
            break;
        case 1:
            order.setOrder("Drink");
            break;
        default:
            order.setOrder("Help");
            break;
        }

        System.out.println(this.toString() + " request " + order.getOrder() + " to " + this.waiter.toString());

        // only the call to the orderqueue is synchronized
        try {
            synchronized (orderQueue) {
                orderQueue.put(order);
                orderQueue.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void receiveRequest() {
        System.out.println(this + " has received " + this.order + " from " + this.waiter);
    }

    public void consumer() {
        System.out.println(this + " was consumed " + this.order);
    }

    @Override
    public String toString() {
        return "Customer: " + id;
    }
}

最后是服务员班:

package room;

public class Waiter extends Thread {

    private int id;
    private Order custOrder;
    private Customer customer;
    private volatile boolean isAvailable;
    private volatile boolean clockOut;
    private OrderQueue<Order> orderQueue; // The queue again

    public Waiter(int id, OrderQueue<Order> orderQueue) {
        this.id = id;
        this.orderQueue = orderQueue;
        System.out.println("Waiter " + id + " is ready to serve!");
    }

    @Override
    public void run() {
        isAvailable = true;
        while (!orderQueue.isDone() && !clockOut) {
            if (isAvailable) { // this waiter is ready for a task
                registerOrder();
                isAvailable = false;
                synchronized (orderQueue) { // Synchronize this to the queue to prevent worse glitches
                    Saloon.round();
                }
                if (custOrder != null) { // error checking
                    System.out.println(this.toString() + " record " + custOrder.toString() + " to " + customer);
                    deliveryRequest();
                } else {
                    isAvailable = true;
                }
            }
        }
    }

    public synchronized void registerOrder() {
        try {
            custOrder = orderQueue.take(); // this is how you pull the order
        } catch (InterruptedException e1) {
            System.out.println(true);
        }
    }

    public synchronized void deliveryRequest() {
        System.out.println(this.toString() + " " + "delivered " + custOrder.toString() + " to " + customer);
        isAvailable = true;
    }

    public void addCustomer(Customer customer) { // a easy way to add customers to the waiter after creation
        this.customer = customer;
        isAvailable = false;
    }

    public Order getOrder() {
        return custOrder;
    }

    @Override
    public String toString() {
        return "Waiter: " + id;
    }

    public boolean isAvailable() {
        return isAvailable;
    }

    public void clockOut() {
        clockOut = true;
    }

    public boolean isClockedOut() {
        return clockOut;
    }
}