第二十一章:并发(下)

新类库中的构件

CountDownLatch

  • 可以向CountDownLatch对象设置一个初始计数值,任何对象在这个对象上调用await()方法都将阻塞,直至这个计数值为0(如果一开始就是0那就不用等待)。我们通过其他任务在这个对象上调用countDown()来缩小这个计数值(最多减到0)。这个构件的意义就是设置一些任务必须在n个任务都完成的情况下才可以执行。
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchDemo {
    static final int SIZE = 100;
    public static void main(String args[]) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        CountDownLatch latch = new CountDownLatch(SIZE);
        for (int i=0; i<10; i++) {
            exec.execute(new WaitingTask(latch));
        }
        for (int i=0; i<SIZE; i++) {
            exec.execute(new TaskPortion(latch));
        }
        System.out.println("Launched all tasks");
        exec.shutdown();
    }
}
class TaskPortion implements Runnable {
    private static int counter = 1;
    private static Random rand = new Random(47);
    private final int id = counter++;
    private final CountDownLatch latch;
    public TaskPortion(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(rand.nextInt(2000));//模拟一个任务需要时间去完成
            System.out.println(this + "completed");
            latch.countDown();
            System.out.println(latch.getCount());
        } catch (InterruptedException e) {}
    }
    public String toString() {
        return String.format("%-3d ", id);
    }
}
class WaitingTask implements Runnable {
    private static int counter = 1;
    private final int id = counter++;
    private final CountDownLatch latch;
    public WaitingTask(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        try {
            latch.await();
            System.out.println("Latch barrier passed for " + this);
        } catch (InterruptedException e) {
            System.out.println(this + "interrupted");
        }
    }
    public String toString() {
        return String.format("WaitingTask %-3d ", id);
    }
}
  • 程序运行后,10个WaitingTask任务将等待,直到100个TaskPortion全部执行完毕再开始执行。

CyclicBarrier

  • CyclicBarrier适用于你希望创建一组任务,它们并行执行,然后在进行下一个步骤之前等待,直至所有任务都完成。它使得所有的并行任务都将在栅栏处列队,因此可以一致地向前运动。它与CountDownLatch的区别是,后者是只触发一次的事件,而它可以多次重用。下面是我模仿书上赛马的程序写的一个关于加载的程序:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 模拟LOL要10个人才能开始游戏
 * 每次必须满10个人一起加载一次,当加载量都为100时,游戏开始
 */
public class PlayGame {
    public static ExecutorService exec = Executors.newCachedThreadPool();
    public static List<Player> players = new ArrayList<Player>();
    public static CyclicBarrier barrier = new CyclicBarrier(10, new StartGame(players));
    static {
        for (int i=0; i<10; i++) {
            Player p = new Player();
            players.add(p);
            System.out.println("player" + p.id + " 准备进入游戏");
        }
    }
    public static void main(String args[]) {
        for (int i=0; i<10; i++)
            exec.execute(new LoadGame(players.get(i), barrier));
    }
}
class Player {
    private static int counter = 1;
    private int percentage = 0;
    public final int id = counter++;
    public void addPercentage(int nextInt) {
        percentage += nextInt;
        System.out.println("player" + id + " 加载了" + percentage + "%");
    }
    public int getPercentage() {return percentage;}
}
class LoadGame implements Runnable {

    private static Random rand = new Random(47);
    private final CyclicBarrier barrier;
    private final Player player;
    public LoadGame(Player player, CyclicBarrier barrier) {
        this.player = player;
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                if (player.getPercentage() < 96) {
                    player.addPercentage(rand.nextInt(5) + 1);//1~5
                } else {
                    player.addPercentage(100 - player.getPercentage());
                }
                barrier.await();
            }
        } catch (InterruptedException e) {
            System.out.println();
        } catch (BrokenBarrierException e) {
            throw new RuntimeException(e);
        }
        System.out.println("player" + player.id + " 开始游戏");
    }

}
class StartGame implements Runnable {
    private List<Player> players;
    public StartGame(List<Player> players) {
        this.players = players;
    }
    @Override
    public void run() {
        System.out.println("服务器集中加载一次完毕!");
        int i = 0;
        for (Player player : players) {
            if (player.getPercentage() < 100) {
                break;
            }
            i++;
        }
        if (i == players.size()) {
            System.out.println("比赛开始!");
            PlayGame.exec.shutdownNow();
        }
    }

}

DelayQueue

  • 这个是一个延迟队列,他也是线程安全的,存放的泛型类型必须实现Delayed接口。当其实它和优先级队列挺像的,下面是一个例子:
import java.util.*;
import java.util.concurrent.*;

class DelayedTask implements Runnable, Delayed {
    private final long delay;
    private final long runtime;
    private final int id;
    public DelayedTask(int id, long delay) {
        this.id = id;
        this.delay = delay;
        this.runtime = System.currentTimeMillis() + delay;
    }

    @Override
    public int compareTo(Delayed o) {
        DelayedTask t = (DelayedTask) o;
        long result = this.runtime -  t.runtime;
        if (result > 0) return 1;
        else if (result < 0) return -1;
        else return 0;
    }
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(runtime - System.currentTimeMillis(),
                TimeUnit.MILLISECONDS);
    }
    @Override
    public void run() {
        System.out.println(id + " run! delay=" + delay);
    }
}
class DelayedTaskConsumer implements Runnable {
    private DelayQueue<DelayedTask> q;
    public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
        this.q = q;
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                q.take().run();//只是run 没有start
            }
        } catch (InterruptedException e) {}
        finally {
            System.out.println("finish!");
        }
    }
}
public class DelayQueueDemo {
    public static void main(String[] args) throws Exception {
        Random rand = new Random(47);
        ExecutorService exec = Executors.newCachedThreadPool();
        DelayQueue<DelayedTask> queue =  new DelayQueue<DelayedTask>();
        for (int i=0; i<20; i++) {
            queue.put(new DelayedTask(i + 1, rand.nextInt(5000)));
        }
        exec.execute(new DelayedTaskConsumer(queue));
        Thread.sleep(5000);
        exec.shutdownNow();
    }
}

PriorityBlockingQueue

  • 如果你看过之前集合中的PriorityQueue, 再结合BlockingQueue的用法,我想这个可以省略了。

ScheduledExecutor

  • 这是一种定时器的方式,类似于js中的setTimeout()setInterval()。直接看例子吧。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ScheduleThreadPoolDemo {
    private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss SSS");
    public static void main(String args[]) {
        ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(5);
        exec.schedule(new Mission(), 1000, TimeUnit.MILLISECONDS);
        //这个是根据上一次开始时间去延迟1000毫秒
        exec.scheduleAtFixedRate(new Mission(), 2000, 1000, TimeUnit.MILLISECONDS);
        //这个是根据上一次结束时间去延迟1000毫秒
        exec.scheduleWithFixedDelay(new Mission(), 3000, 1000, TimeUnit.MILLISECONDS);

        //用法一致 其实返回的是同一种类型
        //ScheduledExecutorService exec2 = Executors.newScheduledThreadPool(5);
        //exec2.schedule(command, delay, unit)
        //exec2.scheduleAtFixedRate(command, initialDelay, period, unit)
        //exec2.scheduleWithFixedDelay(command, initialDelay, delay, unit)
    }
    static class Mission implements Runnable {
        private static int count = 0;
        private final int id = ++count;
        @Override
        public void run() {
            System.out.println(id + " run in:" + 
                    sdf.format(new Date(System.currentTimeMillis())));
        }
    }
}

Semaphore

  • 正常的锁(locks或synchronized)在任何时刻都只允许一个任务访问一项资源。而计数信号量允许n个任务同时访问这个资源(这个资源可能是一个对象池)。
import java.lang.reflect.Array;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    private static final int poolsize = 3;
    public static void main(String args[]) throws Exception {
        Pool<TestObj> pool = new Pool<TestObj>(TestObj.class, poolsize);
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i=0; i<poolsize; i++) {
            exec.execute(new Hold<TestObj>(pool, (i + 1) * 1000));
        } 
        for (int i=0; i<poolsize; i++) {
            exec.execute(new Hold<TestObj>(pool, 500));
        }
        exec.shutdown();
    }
}
class Hold<T> implements Runnable {
    private static int count;
    private final int id = ++count;
    private final Pool<T> pool;
    private final long time;
    public Hold(Pool<T> pool, long time) {
        this.pool = pool;
        this.time = time;
    }
    public void run() {
        try {
            T task = pool.checkOut();
            System.out.println("Thread" + id + " get: " + task);
            Thread.sleep(time);
            System.out.println("Thread" + id + " release: " + task);
            pool.checkIn(task);
            System.out.println("Thread" + id + " release success!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class TestObj {
    private static int count;
    private final int id = ++count;
    public int getId() {
        return id;
    }
    public String toString() {
        return "resourceId:" + id;
    }
}
class Pool<T> {
    private Semaphore available;
    private int size;
    private T[] data;
    private boolean[] checkOut;

    @SuppressWarnings("unchecked")
    public Pool(Class<T> clazz, int size) throws InstantiationException, IllegalAccessException {
        available = new Semaphore(size, true);
        this.size = size;
        checkOut = new boolean[size];
        data = (T[]) Array.newInstance(clazz, size);
        for (int i=0; i<size; i++) {
            data[i] = clazz.newInstance();
        }
    }

    public T checkOut() throws InterruptedException {
        available.acquire();//请求一个许可证,如果没有则等待。
        return getItem();
    }

    public boolean checkIn(T x) throws InterruptedException {
        if (releaseItem(x)) {
            available.release();//释放一个许可证
            return true;
        }
        return false;
    }

    private synchronized T getItem() {
        for (int i=0; i<size; i++) {
            if (!checkOut[i]) {
                checkOut[i] = true;
                return (T) data[i];
            }
        }
        return null;
    }

    private synchronized boolean releaseItem(T x) {
        for (int i=0; i<size; i++) {
            if (data[i] == x) {
                if (checkOut[i]) {
                    checkOut[i] = false;
                    return true;
                }
                return false;
            }
        }
        return false;
    }
}
--------------运行结果:
Thread1 get: resourceId:1
Thread2 get: resourceId:2
Thread3 get: resourceId:3
Thread1 release: resourceId:1
Thread1 release success!
Thread4 get: resourceId:1
Thread4 release: resourceId:1
Thread4 release success!
Thread5 get: resourceId:1
Thread2 release: resourceId:2
Thread2 release success!
Thread6 get: resourceId:2
Thread5 release: resourceId:1
Thread5 release success!
Thread6 release: resourceId:2
Thread6 release success!
Thread3 release: resourceId:3
Thread3 release success!

Exchanger

  • Exchanger是在两个任务之间交换对象的栅栏。他们各自拥有一个对象,调用exchange()后(可能进入阻塞,等待对方调用这个方法),会得到对方的对象。Exchanger的应用场景是:一个任务在创建对象,这些对象的生产代价很高昂。而另外一个任务在消费这些对象(然后将空容器返回给生产者)。听起来和普通的生产者消费者差不多,区别就是可以在消费这个对象的同时创建新的对象(因代价高昂需尽早创建,且因为调用exchange()会阻塞,也不必担心浪费空间)。可以想象这么一个场景:一共有两个水桶,一个人负责在井边打水,一个人负责拿着装满水的桶去装进自家水库。要是只有一个水桶,打水的和倒水的难免得互相等待。如果有两个桶,倒水的每次只要将空桶交给打水的,然后直接拿走装满水的桶,那么就可以使效率提高。
import java.util.List;
import java.util.concurrent.*;

public class ExchangerDemo {
    public static void main(String args[]) {
        Exchanger<List<Integer>> changer = new Exchanger<List<Integer>>();
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExchangerProducer(changer));
        exec.execute(new ExchangerConsumer(changer));
        exec.shutdown();
    }
}

class ExchangerProducer implements Runnable {
    //这个类型可以在遍历的时候,remove。因为是交换对象,所以生产者的类型一般都和消费者一致
    private List<Integer> holder = new CopyOnWriteArrayList<>();
    private int begin = 0;
    private Exchanger<List<Integer>> changer;
    public ExchangerProducer(Exchanger<List<Integer>> changer) {
        this.changer = changer;
    }
    @Override   
    public void run() {
        try {
            while (!Thread.interrupted()) {
                for (int i=begin; i<begin+10; i++) {
                    holder.add(i);
                }
                begin = begin + 10;
                System.out.println("producer 前 " + holder);
                holder = changer.exchange(holder);
                System.out.println("producer 后 " + holder);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
class ExchangerConsumer implements Runnable {
    private List<Integer> holder = new CopyOnWriteArrayList<>();//这个类型可以在遍历的时候,remove
    private Exchanger<List<Integer>> changer;
    public ExchangerConsumer(Exchanger<List<Integer>> changer) {
        this.changer = changer;
    }
    @Override   
    public void run() {
        try {
            while (!Thread.interrupted()) {
                System.out.println("consumer 前 " + holder);
                holder = changer.exchange(holder);
                System.out.println("consumer 后 " + holder);
                for (Integer i : holder) {
                    //holder.remove(i);//注释掉可以看到交换场景
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

仿真

银行出纳员仿真

  • 看这个名字差不多就知道是什么用处,代码有注释。期间遇到了怪异的问题(出队不是最大值),后来看了看以前的文章就明白了。
import java.util.*;
import java.util.concurrent.*;

public class BankTellerSimulation {
    private static final int MAX_LINE_SIZE = 50;
    private static final int ADJUSTMENT_PERIOD = 1000;
    public static void main(String args[]) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        CustomerLine customers = new CustomerLine(MAX_LINE_SIZE);
        exec.execute(new CustomerGenerator(customers));
        exec.execute(new TellerManager(exec, customers, ADJUSTMENT_PERIOD));
        System.in.read();
        exec.shutdownNow();
    }
}
//顾客
class Customer {
    private final int serviceTime;//此值模拟用户的服务时间
    public Customer(int st) {
        serviceTime = st;
    }
    public int getServiceTime() {
        return serviceTime;
    }
    public String toString() {
        return "[" + serviceTime + "]";
    }
}
//排号队伍
class CustomerLine extends ArrayBlockingQueue<Customer> {
    public CustomerLine(int capacity) {
        super(capacity);
    }
    public String toString() {
        if (isEmpty()) {
            return "[Empty]";
        }
        StringBuilder result = new StringBuilder();
        for (Customer cus : this) {
            result.append(cus);
        }
        return result.toString();
    }
}
//模拟有顾客来办业务
class CustomerGenerator implements Runnable {
    private static Random random = new Random(47);
    private CustomerLine customers;
    public CustomerGenerator(CustomerLine cl) {
        customers = cl;
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Thread.sleep(random.nextInt(300));
                //不定期的来客户,其服务时间也不定
                customers.put(new Customer(random.nextInt(1000)));
            }
        } catch (InterruptedException e) {
            System.out.println("CustomerGenerator 程序中断");
        }
        System.out.println("CustomerGenerator 程序停止");
    }
}
//出纳员服务线程
class Teller implements Runnable, Comparable<Teller>{
    private static int counter = 0;
    private final int id = counter++;

    private int customerServed = 0;//已服务人数
    private CustomerLine customers;
    private boolean servingCustomerLine = true;
    public Teller(CustomerLine cl) {
        customers = cl;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Customer customer = customers.take();//呼叫客户,如果没有则阻塞
                Thread.sleep(customer.getServiceTime());//模拟进行服务
                //在服务期间TellerManager线程可能判断出此时不再需要那么多人了,servingCustomerLine被置为false
                synchronized (this) {
                    customerServed++;//服务人数加1
                    //如果此时TellerManager线程已经允许我们释放自己,那么我们就wait()。
                    //用while是因为唤醒的可能是别人而不是自己
                    while (!servingCustomerLine)
                        wait();//wait期间TellerManager线程可能又将其设置为true
                }
            }
        } catch (InterruptedException e) {
            System.out.println("Teller 程序中断");
        }
        System.out.println("Teller 程序停止");

    }
    //没人来了就等待,或者说去做别的事情去
    public synchronized void doSomethingElse() {
        //customerServed = 0;//书上有这句代码,我觉得没有必要,因为讲道理是可以累积的
        servingCustomerLine = false;//将这个出纳员的状态调整为休息状态
        //吃饭聊天打豆豆...爱干嘛干嘛
    }
    //有人来了就开始服务
    public synchronized void serveCustomerLine() {
        assert !servingCustomerLine : "服务已经在运行!:" + this;
        servingCustomerLine = true;
        notifyAll();
    }
    //用于线程资源合理分配,干活多的出纳员优先出队去干别的事情。比较期间不允许对customerServed进行修改,所以是synchronized的。
    public synchronized int compareTo(Teller o) {
        return customerServed - o.customerServed;
    }
    //出纳员编号
    public String toString() {
        return "T" + id + "(" + customerServed + ")";
    }
}

//出纳员控制系统
class TellerManager implements Runnable {
    private ExecutorService exec;
    private CustomerLine customers;
    //了解PriorityQueue的应该知道,当poll或者offer一个值时,它并不会遍历所有的队列值
    //此处书上使用PriorityQueue是不对的,因为customerServed在中途进行了变化,为了使结果正确我将重写poll方法。
    //private PriorityQueue<Teller> workingTellers = new PriorityQueue<Teller>();
    private PriorityQueue<Teller> workingTellers = new MyPriorityQueue<Teller>();
    //这个队列也不能这么写,也应该选择最小的出来,不过因为这个队列里的customerServed不会变化,所以可以直接使用PriorityQueue
    //其实就算改成PriorityQueue也不是特别严谨,要注意到customerServed++基本上是在放入队列后发生的。不过最多差1位,关系不是很大
    //这里也体现了要想正确使用并发编程真的很难
    //private Queue<Teller> tellerDoingOhterThings = new LinkedList<Teller>();
    private Queue<Teller> tellerDoingOhterThings = new PriorityQueue<Teller>();
    private int adjustmentPeriod;//调整时间间隔
    public TellerManager(ExecutorService e,
            CustomerLine cl, int adjustmentPeriod) {
        exec = e;
        customers = cl;
        this.adjustmentPeriod = adjustmentPeriod;
        newOneTeller();
    }
    public void adjustTellerNumber() {
        //如果顾客太多了,就增加一个出纳员
        if (customers.size() / workingTellers.size() > 2) {
            if (tellerDoingOhterThings.size() > 0) {
                Teller teller = tellerDoingOhterThings.poll();//选出一个最小值
                teller.serveCustomerLine();//从空闲队列中取出一个出纳员进行服务,必须持有teller的锁
                workingTellers.offer(teller);
                return;
            }
            newOneTeller();
            return;
        }
        //如果工作的人太多了,就释放一个出纳员
        if (workingTellers.size() > 1 && customers.size() / workingTellers.size() < 2) {
            reassingOneTeller();
        }
        //如果没有要服务的客户,则递减到一个出纳员
        if (customers.size() == 0) {
            while (workingTellers.size() > 1) {
                reassingOneTeller();
            }
        }
    }
    //分配一个出纳员
    private void newOneTeller() {
        Teller teller = new Teller(customers);
        exec.execute(teller);
        workingTellers.add(teller);
    }
    //释放一个出纳员
    private void reassingOneTeller() {
        Teller teller = workingTellers.poll();//使用了重写的方法,选出了一个最大值
        teller.doSomethingElse();//必须持有teller的锁
        tellerDoingOhterThings.offer(teller);
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Thread.sleep(adjustmentPeriod);
                adjustTellerNumber();//因为只有一个线程,所以不需要加锁
                System.out.print(customers + " {工作中: ");
                for (Teller teller : workingTellers) {
                    System.out.print(teller + " ");
                }
                System.out.print(", 休息中: ");
                for (Teller teller : tellerDoingOhterThings) {
                    System.out.print(teller + " ");
                }
                System.out.println("}");
            }
        } catch (InterruptedException e) {
            System.out.println("TellerManager 程序中断");
        }
        System.out.println("TellerManager 程序结束");
    }
}

//修改poll方法的优先级队列
class MyPriorityQueue<T> extends PriorityQueue<T> {
    @Override
    public T poll() {
        if (isEmpty()) return null;
        T[] data = (T[]) toArray();
        T max = data[0];
        for (int i = 1; i < data.length; i++) {
            if (((Comparable<T>) max).compareTo(data[i]) < 0) {
                max = data[i];
            }
        }
        remove(max);
        return max;
    }
}

饭店仿真

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

public class RestaurantWithQueues {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        Restaurant rest = new Restaurant(exec, 5, 2);//5个小二,两个厨师
        exec.execute(rest);//开业啦
        Thread.sleep(10000);//开十秒就关门了
        exec.shutdownNow();//所有人都赶出去,最可怕的是连厨师小二都不让干活了
    }
}
//可以假想成一份菜单,不过是一份菜的指派菜单
class Order {
    private static int counter = 0;
    private final int id = counter++;
    private final Customer customer;//顾客
    private final WaitPerson waitPerson;//服务的小二
    private final Food food;//所需的菜肴
    public Order(Customer customer, WaitPerson waitPerson, Food food) {
        this.customer = customer;
        this.waitPerson = waitPerson;
        this.food = food;
    }
    public Food item() {
        return food;
    }
    public Customer getCustomer() {
        return customer;
    }
    public WaitPerson getWaitPerson() {
        return waitPerson;
    }
    public String toString() {
        return "Order: " + id + " item: " + food + 
                " for: " + customer + " served by: " + waitPerson;
    }
}
//可以假想成一份成品菜肴,可以吃的那种
class Plate {
    private final Order order;//对应的菜单
    private final Food food;//可以吃的菜肴,这里用枚举类代替
    public Plate(Order order, Food food) {
        this.order = order;
        this.food = food;
    }
    public Order getOrder() {
        return order;
    }
    public Food getFood() {
        return food;
    }
    public String toString() {
        return food.toString();
    }
}
//顾客 线程,任务是给小二下单,然后等待小二送来菜肴
class Customer implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final WaitPerson waitPerson;//该服务的小二
    private static Random rand = new Random(47);
    private SynchronousQueue<Plate> plateSetting =
            new SynchronousQueue<Plate>();//只有take和put同时完成后,才能进行下次take()和put(),否则任一操作都会阻塞
    public Customer(WaitPerson wp) {
        waitPerson = wp;
    }
    public void deliver(Plate p) throws InterruptedException {
        plateSetting.put(p);//从小二的手中接过菜肴(put->take 组成一个过程)
    }
    @Override
    public void run() {
        Food[] foods = Food.choose();//顾客随机选取了一组菜肴
        System.out.println(this + "下单:" + Arrays.toString(foods));
        try {
            if (!Thread.interrupted())  {
                for (Food food : foods) {
                    waitPerson.placeOrder(this, food);//一个一个上
                    System.out.println(this + "eating " + plateSetting.take());//等待直到 从小二的手中接过菜肴(put->take组成一个过程)。
                }
            }
        } catch (InterruptedException e) {
            System.out.println(this + "waiting 中断");
        }
        System.out.println(this + "吃完了");
    }
    public String toString() {
        return "Customer " + id + " ";
    }
}
//小二 线程,任务是拿出已经做好并且是分发给自己的菜肴,然后送给这个顾客
class WaitPerson implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final Restaurant restaurant;
    BlockingQueue<Plate> filledOrders = 
            new LinkedBlockingQueue<Plate>();//小二收到的已经完成的菜肴
    public WaitPerson(Restaurant r) {
        restaurant = r;
    }
    public void placeOrder(Customer cust, Food food) {
        try {
            restaurant.orders.put(new Order(cust, this, food));//添加一份新订单
        } catch (InterruptedException e) {
            System.out.println(this + " placeOrder 中断");
        }
    }
    public void run() {
        try {
            while (!Thread.interrupted()) {//压榨他!不许休息
                Plate plate = filledOrders.take();//拿出包装好的菜肴
                System.out.println(this + "received " + plate 
                        + " delivering to " + plate.getOrder().getCustomer());//准备丢给那个顾客
                plate.getOrder().getCustomer().deliver(plate);//让顾客接收这个菜肴
            }
        } catch (InterruptedException e) {
            System.out.println(this + " 中断");
        }
        System.out.println(this + " 结束");
    }
    public String toString() {
        return "WaitPerson " + id + " ";
    }
}
//厨师 线程,任务是从订单队列中拿出订单(Order),做完(Plate)并丢给相应的小二
class Chef implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final Restaurant restaurant;
    private static Random rand = new Random(47);
    public Chef(Restaurant r) {
        restaurant = r;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {//压榨他!不许休息
                Order order = restaurant.orders.take();//厨师从菜单中拿出Order,也就是订单
                Food item = order.item();//知道要做什么菜
                Thread.sleep(rand.nextInt(500));//假设花的时间不定
                Plate plate = new Plate(order, item);//成品菜肴完成了
                order.getWaitPerson().filledOrders.put(plate);//丢给这个小二
            }
        } catch (InterruptedException e) {
            System.out.println(this + " 中断");
        }
        System.out.println(this + " 结束");
    }
    public String toString() {
        return "Chef " + id + " ";
    }
}
//饭店 线程 负责接待顾客,分配小二
class Restaurant implements Runnable {
    private List<WaitPerson> waitPersons = new ArrayList<WaitPerson>();
    private List<Chef> chefs = new ArrayList<Chef>();
    private ExecutorService exec;
    private static Random rand = new Random(47);
    BlockingQueue<Order> orders = 
            new LinkedBlockingQueue<Order>();//相当于订单表
    public Restaurant(ExecutorService exec, int nWaitPersons, int nChefs) {
        this.exec = exec;
        for (int i = 0; i < nWaitPersons; i++) {
            WaitPerson waiter = new WaitPerson(this);
            waitPersons.add(waiter);
            exec.execute(waiter);
        }
        for (int i = 0; i < nChefs; i++) {
            Chef chef = new Chef(this);
            chefs.add(chef);
            exec.execute(chef);
        }
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {//我就是不关门
                WaitPerson wp = waitPersons.get(rand.nextInt(waitPersons.size()));//随机选取一个小二
                Customer c = new Customer(wp);//假设来了一个客户,并把这个客户分给这个小二
                exec.execute(c);//让这个客户自行下单
                Thread.sleep(100);//模拟0.1秒来个客户
            }
        } catch (InterruptedException e) {
            System.out.println("Restaurant 中断");
        }
        System.out.println("Restaurant 结束");
    }
}
enum Food {
    南汇水蜜桃, 三林糖酱瓜, 佘山兰笋, 松江回鳃鲈, 枫泾西蹄, 城隍庙五香豆, 崇明金瓜, 南桥乳腐, 高桥松饼, 嘉定大白蒜;
    private static Random rand = new Random(47);
    public static Food[] choose() {
        Food[] allfoods = values();
        int count = allfoods.length;//10
        for (int i = 0; i < count - 1; i++) { //打乱顺序
            int index = rand.nextInt(count);
            Food temp = allfoods[i];
            allfoods[i] = allfoods[index];
            allfoods[index] = temp;
        }
        int need = rand.nextInt(allfoods.length / 2) + 1;
        Food[] foods = new Food[need];
        int beginIndex = rand.nextInt(count - need + 1);
        System.arraycopy(allfoods, beginIndex, foods, 0, need);
        return foods;
    }
}

分发工作

性能调优

比较各类互斥技术

免锁容器

乐观锁

比较各类Map实现

乐观加锁

ReadWriteLock

活动对象

小结

  • 还有这么多的内容,我就不写了。。想开启下一本书,这个就先这样吧。以后想起来了再回来更新。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值