细粒度的线程控制?使用Lock Condition~

这里举一个栗子,我们对一个资源进行加锁,可是又要进行细粒度的控制,该如何实现呢?

比如我们开了了个餐馆。餐馆有一个厨房,服务员可以通知厨房进行做菜,当前冰箱里有菜时,厨房就会开始做菜,冰箱里没菜则会等待。

/**
 * Created by Anur IjuoKaruKas on 6/28/2018
 */
@SuppressWarnings("Duplicates")
public class Restaurant {

    private final Lock kitchen = new ReentrantLock();

    private ConcurrentLinkedDeque<String> meetFridge = new ConcurrentLinkedDeque<>();// 肉冰箱

    public Runnable cockMeet() {
        return new Runnable() {

            @Override
            public void run() {

                synchronized (kitchen) {
                    System.out.println("通知厨房做肉");
                    if (meetFridge.isEmpty()) {
                        try {
                            System.out.println("冰箱没有肉了,等待冰箱有肉");
                            kitchen.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String meetNeedToCock = meetFridge.getFirst();
                    System.out.println("正在炒" + meetNeedToCock);
                }
            }
        };
    }

    public Runnable buySomething() {
        return new Runnable() {

            @Override
            public void run() {

                synchronized (kitchen) {
                    System.out.println("进货了");
                    meetFridge.addLast("牛肉");
                    kitchen.notify();
                }
            }
        };
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        Restaurant restaurant = new Restaurant();

        executorService.execute(restaurant.cockMeet());
        executorService.execute(restaurant.cockMeet());

        Thread.sleep(1000);
        executorService.execute(restaurant.buySomething());
        Thread.sleep(1000);
        executorService.execute(restaurant.buySomething());
        Thread.sleep(1000);
        executorService.execute(restaurant.buySomething());

        executorService.execute(restaurant.cockMeet());
    }
}

运行一下main方法,可以得到以下输出:

通知厨房做肉
冰箱没有肉了,等待冰箱有肉
通知厨房做肉
冰箱没有肉了,等待冰箱有肉
进货了
正在炒牛肉
进货了
正在炒牛肉
进货了
通知厨房做肉
正在炒牛肉

到这里是没有什么问题的。

进来了一个新需求,一个刚好可以用上Condition的新需求

现在我们既需要做肉,也需要做菜。
也就是说:
1、服务员通知了厨房,需要做一个肉和一个菜。这个时候厨房正好没库存,厨房进行了等待。
2、这时候某人去菜市场买了菜回来,厨房开始做菜。
3、过了一段时间
4、某人去菜市场买了肉回来,厨房开始做肉。

这样的一个需求,当然用其他方式实现也是可以的,但如果使用 Condition来实现,它将变得异常简单。
/**
 * Created by Anur IjuoKaruKas on 6/28/2018
 */
@SuppressWarnings("Duplicates")
public class Restaurant {

    private final Lock kitchen = new ReentrantLock();

    private final Condition waitMeet = kitchen.newCondition();

    private final Condition waitVege = kitchen.newCondition();

    private ConcurrentLinkedDeque<String> meetFridge = new ConcurrentLinkedDeque<>();// 肉冰箱

    private ConcurrentLinkedDeque<String> vegeFridge = new ConcurrentLinkedDeque<>();// 菜冰箱

    public Runnable cockMeet() {
        return new Runnable() {

            @Override
            public void run() {
                kitchen.lock();
                try {
                    System.out.println("通知厨房做肉");
                    if (meetFridge.isEmpty()) {
                        try {
                            System.out.println("冰箱没有肉了,等待冰箱有肉");
                            waitMeet.await();   // 直接调用condition的wait方法
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String meetNeedToCock = meetFridge.getFirst();
                    System.out.println("正在炒" + meetNeedToCock);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    kitchen.unlock();
                }
            }
        };
    }

    public Runnable cockVege() {
        return new Runnable() {

            @Override
            public void run() {
                kitchen.lock();
                try {
                    System.out.println("通知厨房做菜");
                    if (vegeFridge.isEmpty()) {
                        try {
                            System.out.println("冰箱没有菜了,等待冰箱有菜");
                            waitVege.await();   // 直接调用condition的wait方法
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String meetNeedToCock = vegeFridge.getFirst();
                    System.out.println("正在炒" + meetNeedToCock);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    kitchen.unlock();
                }
            }
        };
    }

    public Runnable buySomething() {
        return new Runnable() {

            @Override
            public void run() {
                kitchen.lock();
                try {
                    Random random = new Random();

                    if (random.nextBoolean()) {
                        System.out.println("肉进货了");
                        meetFridge.addLast("牛肉");
                        waitMeet.signal();
                    } else {
                        System.out.println("菜进货了");
                        vegeFridge.addLast("苦瓜");
                        waitVege.signal();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    kitchen.unlock();
                }
            }
        };
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        Restaurant restaurant = new Restaurant();

        executorService.execute(restaurant.cockMeet());
        executorService.execute(restaurant.cockVege());

        executorService.execute(restaurant.buySomething());
    }
}

最后输出:

通知厨房做肉
冰箱没有肉了,等待冰箱有肉
通知厨房做菜
冰箱没有菜了,等待冰箱有菜
肉进货了
正在炒牛肉

可见我们可以针对情况对不同的行为进行通知,这就是condition的力量。

提高篇

这里就不瞎扯场景了,直接上代码。

这是仿kafka BufferPool的一种思路,(当然没kafka实现的那么复杂),它的思路是使用一个队列来管理等待的线程。
每次线程进来sout(),都进行等待
满足一定的条件时,mission()会通知队头的一个线程进行操作。

/**
 * Created by Anur IjuoKaruKas on 6/25/2018
 */
public class Task {

    private Deque<Condition> waiters = new ArrayDeque<>();

    private Lock lock = new ReentrantLock();

    private Integer count = 0;

    private void sout(String str) {
        this.lock.lock();
        try {
            System.out.println("sout " + str + " get the lock");
            Condition condition = this.lock.newCondition();

            waiters.addLast(condition);
            condition.await();

            Condition conditionFromWaiters = waiters.removeFirst();

            if (conditionFromWaiters != condition) {
                System.out.println("???????");
            }

            System.out.println("Test Task: " + str);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void mission() {
        this.lock.lock();
        try {
            System.out.println("mission get the lock");
            while (count < 10) {
                count++;
            }
            Condition condition = waiters.peekFirst();
            if (condition != null) {
                condition.signal();
            }
            count = 0;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        final Task task = new Task();

        for (int i = 0; i < 1000000; i++) {
            final int finalI = i;

            executorService.execute(new Runnable() {

                @Override
                public void run() {
                    task.sout(finalI + "");
                }
            });

            executorService.execute(new Runnable() {

                @Override
                public void run() {
                    task.mission();
                }
            });
        }
    }
}

猜你喜欢

转载自blog.csdn.net/anurnomeru/article/details/80843123