Java Lock应用示例-生产者与消费者

Java Lock的使用以及与synchronized的区别很多文章已经讲解的很清楚了,这里不再详细讲解,可以参考java Lock讲解.
总的来说Lock的功能比synchronized更强大,功能更多,但一般的线程同步业务synchronized已经能够满足,对于一些特殊的要求,比如要知道线程获取锁的结果,线程获取锁时没有获取到,要求等待一段时间后仍没获取到就不去获取了,去做别的事情或者等待锁的过程中能够响应中断等等。Lock接口中的newCondition()方法用于获取锁对象上绑定的实例,用于线程的等待与唤醒,可以获取多个实例,而不是像synchronized只有一个锁对象(此时锁对象既是锁对象也是实例),好处是线程可以使用不同的实例,进行有针对的等待与唤醒,Lock接口唯一的实现类是ReentrantLock,下面我使用这个类来实现经典的生产者与消费者。

package cn.yanggy.demo04;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * User:cool coding
 * Date:2018/1/10
 * Time:12:44
 * 篮子
 * 生产者和消费者需要向蓝子申请锁
 */
public class Bucket {
    /**
     * 蓝子
     */
    private List<String> buckets;

    /**
     * 蓝子的容量
     */
    private final int maxCount=20;

    /**
     * 蓝子上的锁
     */
    private final Lock lock=new ReentrantLock();

    /**
     * 消费锁对象
     */
    private final Condition fetchLock;

    /**
     * 放入锁对象
     */
    private final Condition addLock;

    public Bucket(){
        buckets=new ArrayList<>();
        fetchLock =lock.newCondition();
        addLock =lock.newCondition();
    }

    /**
     * 获得锁
     * @return
     */
    public Lock getLock() {
        return lock;
    }

    /**
     * 获得消费锁对象
     * @return
     */
    public Condition getFetchLock() {
        return fetchLock;
    }

    /**
     * 获得放入锁对象
     * @return
     */
    public Condition getAddLock() {
        return addLock;
    }

    /**
     * 蓝子当前大小
     * @return
     */
    public int size(){
        return buckets.size();
    }

    /**
     * 放入
     * @param obj
     */
    public boolean add(String obj){
        if(buckets.size()<20) {
            buckets.add(obj);
            return true;
        }
        else return false;
    }

    /**
     * 随机获取一个蓝子里的东西
     * @return
     */
    public String fetch(){
        int size=buckets.size();
        if(size==1){
            String obj=buckets.get(0);
            buckets.clear();
            return obj;
        }
        else if(size>1) {
            int index = new Random().nextInt(size);
            String obj=buckets.get(index);
            buckets.remove(index);
            return obj;
        }else return null;
    }
}

package cn.yanggy.demo04;

import java.util.Random;

/**
 * User:cool coding
 * Date:2018/1/10
 * Time:12:49
 * 生产苹果类
 */
public class Produce {

    /**
     * 生产苹果,耗时[1,5]秒(多线程共用)
     * @return
     */
    public static String produce(){
        try {
            Thread.sleep(new Random().nextInt(5)*1000+1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "苹果";
    }
}

package cn.yanggy.demo04;

import java.awt.*;
import java.util.Random;

/**
 * User:cool coding
 * Date:2018/1/10
 * Time:12:53
 * 消费者
 */
public class Consumer implements Runnable {

    /**
     * 篮子
     */
    private Bucket bucket;

    public Consumer(Bucket bucket){
        this.bucket=bucket;
    }


    @Override
    public void run() {
        while (true){
                //拿锁
                bucket.getLock().lock();
                String obj=bucket.fetch();
                if(obj==null) {
                    System.out.println(Thread.currentThread().getName()+":篮子空了");
                    //通知生产者生产
                    bucket.getAddLock().signalAll();
                    //消费者等待
                    try {
                        bucket.getFetchLock().await();
                    }catch (InterruptedException e){

                    }finally {
                        bucket.getLock().unlock();
                    }
                }
                else{
                    System.out.println(Thread.currentThread().getName()+":消费了"+obj);
                    bucket.getLock().unlock();

                    //消费完后,随机休息[1,5]秒钟
                    try {
                        Thread.sleep(new Random().nextInt(5)*1000+1000);
                    }catch (InterruptedException e){

                    }
                }
        }
    }
}

package cn.yanggy.demo04;

import java.util.concurrent.ThreadLocalRandom;

/**
 * User:cool coding
 * Date:2018/1/10
 * Time:13:02
 * 生产者
* 分两步:先生产,然后放入蓝子
 */
public class Producer implements Runnable {
    /**
     * 蓝子
     */
    private final Bucket bucket;

    /**
     * 当前手上拿着的东西
     */
    private ThreadLocal<String> currentHold=new ThreadLocal<>();
    /**
     * 记录总共生产出来东西的数量
     */
    private ThreadLocal<Integer> count=new ThreadLocal<>();


    public Producer(Bucket bucket){
        this.bucket=bucket;
    }

    @Override
    public void run() {
        //初始化本地线程执有的变量,不能写在Construct中,否则无效
        currentHold.set(null);
        count.set(0);

        while (true){
            String obj=currentHold.get();
            if(obj==null) {//如果生产者手上没有拿着东西,则生产,否则先要放入篮子中
                obj = Produce.produce();
                count.set(count.get()+1);
                System.out.println(Thread.currentThread().getName() + ":生产了第[" +count.get()+"]个"+obj);
            }
            bucket.getLock().lock();
            boolean result=bucket.add(Thread.currentThread().getName()+"生产的第["+count.get()+"]个"+obj);
            if(result) {
               currentHold.remove();
               System.out.println(Thread.currentThread().getName() + ":放入了第[" +count.get()+"]个"+obj);
                System.out.println("篮子大小为:"+bucket.size());
               bucket.getLock().unlock();
            }
            else {
                currentHold.set(obj);
                System.out.println(Thread.currentThread().getName() + ":篮子满了");
                bucket.getFetchLock().signalAll();//通知消费者去消费
                try {
                    bucket.getAddLock().await();//停止生产
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    bucket.getLock().unlock();
                }
            }
        }
    }
}

package cn.yanggy.demo04;

/**
 * User:cool coding
 * Date:2018/1/10
 * Time:13:16
 *总共产生10个消费者,5个生产者
 */
public class Main {
    public static void main(String[] args){
        Bucket bucket=new Bucket();//篮子

        Consumer consumer=new Consumer(bucket);//消费者
        Producer producer=new Producer(bucket);//生产者

        for(int i=0;i<5;i++){
            Thread thread=new Thread(producer,"P["+i+"]");
            Thread thread2=new Thread(consumer,"C["+i+"]");
            thread.start();
            thread2.start();
        }

        //延迟2秒钟,以便后五个消费者可以消费到东西
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=5;i<10;i++){
            Thread thread=new Thread(consumer,"C["+i+"]");
            thread.start();
        }
    }
}

运行结果:

C[0]:篮子空了
C[1]:篮子空了
C[2]:篮子空了
C[3]:篮子空了
C[4]:篮子空了
P[3]:生产了第[1]个苹果
P[3]:放入了第[1]个苹果
篮子大小为:1
C[5]:消费了P[3]生产的第[1]个苹果
C[6]:篮子空了
C[7]:篮子空了
C[8]:篮子空了
C[9]:篮子空了
C[5]:篮子空了
P[3]:生产了第[2]个苹果
P[2]:生产了第[1]个苹果
P[4]:生产了第[1]个苹果
P[3]:放入了第[2]个苹果
篮子大小为:1
P[2]:放入了第[1]个苹果
篮子大小为:2
P[4]:放入了第[1]个苹果
篮子大小为:3
P[0]:生产了第[1]个苹果
P[1]:生产了第[1]个苹果
P[0]:放入了第[1]个苹果
篮子大小为:4
P[1]:放入了第[1]个苹果
篮子大小为:5
P[4]:生产了第[2]个苹果
P[2]:生产了第[2]个苹果
P[4]:放入了第[2]个苹果
篮子大小为:6
P[2]:放入了第[2]个苹果
篮子大小为:7
P[3]:生产了第[3]个苹果
P[3]:放入了第[3]个苹果
篮子大小为:8
P[1]:生产了第[2]个苹果
P[1]:放入了第[2]个苹果
篮子大小为:9
P[0]:生产了第[2]个苹果
P[0]:放入了第[2]个苹果
篮子大小为:10
P[4]:生产了第[3]个苹果
P[4]:放入了第[3]个苹果
篮子大小为:11
P[3]:生产了第[4]个苹果
P[2]:生产了第[3]个苹果
P[3]:放入了第[4]个苹果
篮子大小为:12
P[2]:放入了第[3]个苹果
篮子大小为:13
P[1]:生产了第[3]个苹果
P[1]:放入了第[3]个苹果
篮子大小为:14
P[4]:生产了第[4]个苹果
P[4]:放入了第[4]个苹果
篮子大小为:15
P[0]:生产了第[3]个苹果
P[0]:放入了第[3]个苹果
篮子大小为:16
P[2]:生产了第[4]个苹果
P[2]:放入了第[4]个苹果
篮子大小为:17
P[3]:生产了第[5]个苹果
P[3]:放入了第[5]个苹果
篮子大小为:18
P[1]:生产了第[4]个苹果
P[2]:生产了第[5]个苹果
P[1]:放入了第[4]个苹果
篮子大小为:19
P[2]:放入了第[5]个苹果
篮子大小为:20
P[3]:生产了第[6]个苹果
P[4]:生产了第[5]个苹果
P[3]:篮子满了
P[4]:篮子满了
C[0]:消费了P[3]生产的第[3]个苹果
C[1]:消费了P[3]生产的第[2]个苹果
C[2]:消费了P[4]生产的第[4]个苹果
C[3]:消费了P[3]生产的第[4]个苹果
C[4]:消费了P[2]生产的第[1]个苹果
C[6]:消费了P[0]生产的第[2]个苹果
C[7]:消费了P[3]生产的第[5]个苹果
C[8]:消费了P[2]生产的第[5]个苹果
C[9]:消费了P[4]生产的第[3]个苹果
C[5]:消费了P[4]生产的第[2]个苹果
P[0]:生产了第[4]个苹果
P[0]:放入了第[4]个苹果
篮子大小为:11
P[2]:生产了第[6]个苹果
P[2]:放入了第[6]个苹果
篮子大小为:12
C[9]:消费了P[2]生产的第[2]个苹果
C[8]:消费了P[2]生产的第[4]个苹果
C[2]:消费了P[0]生产的第[4]个苹果
P[0]:生产了第[5]个苹果
P[0]:放入了第[5]个苹果
篮子大小为:10
C[4]:消费了P[2]生产的第[6]个苹果
C[9]:消费了P[0]生产的第[3]个苹果
C[0]:消费了P[4]生产的第[1]个苹果
P[0]:生产了第[6]个苹果
P[0]:放入了第[6]个苹果
篮子大小为:8
P[1]:生产了第[5]个苹果
P[1]:放入了第[5]个苹果
篮子大小为:9
C[6]:消费了P[1]生产的第[4]个苹果
C[0]:消费了P[0]生产的第[5]个苹果
C[9]:消费了P[0]生产的第[1]个苹果
P[2]:生产了第[7]个苹果
C[1]:消费了P[1]生产的第[2]个苹果
P[2]:放入了第[7]个苹果
篮子大小为:6
C[5]:消费了P[1]生产的第[5]个苹果
P[0]:生产了第[7]个苹果
P[0]:放入了第[7]个苹果
篮子大小为:6
......
......
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352