根据Lock和Condition写生产者和消费者以及实现阻塞的消息队列

6 篇文章 0 订阅
4 篇文章 0 订阅

JDK1.5开始引入并发包,由此,并发编程开始了性能提升的旅程

 第一部分:生产者和消费者

描述:在多线程中,存在多线程同时操作一个或者是多个资源的情况,当生产线程生产出来产品,消费线程才可以进行消费,如果当前产品已经不剩余,则消费线程会进入阻塞状态,如果当前产品已经生产完成或者是产品数量达到某一个条件,则生产线程进入阻塞状态,线程之间通过这种协作,能很好的提升资源的利用率

package com.milla.study.netbase.expert.concurrent.lock;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Package: com.milla.study.netbase.expert.concurrent.lock
 * @Description: <生产者消费者>
 * @Author: MILLA
 * @CreateDate: 2020/6/2 13:22
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/6/2 13:22
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class ProducerAndConsumerTest {
    public static void main(String[] args) {
        ProducerAndConsumerTest test = new ProducerAndConsumerTest();
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(800L);
                    test.producer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "producer");

        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(500L);
                    System.out.println(Thread.currentThread().getName() + ",消费数据:" + test.consumer());
                    System.out.println();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "consumer");
        t1.start();
        t2.start();
    }

    //产品code
    private String productCode;
    //锁
    private ReentrantLock lock = new ReentrantLock();
    //消费者信号量
    private Condition consumer = lock.newCondition();
    //生产者信号量
    private Condition producer = lock.newCondition();


    /**
     * 生产者方法
     *
     * @throws InterruptedException
     */
    public void producer() throws InterruptedException {
        //加锁
        lock.lock();
        try {
            //如果当前产品code为空,需要阻塞
            while (!Objects.isNull(productCode)) {
                producer.await();//阻塞
            }
            //TODO 真正的生产数据(模拟)
            productCode = UUID.randomUUID().toString();
            System.out.println(Thread.currentThread().getName() + ",生产数据:" + productCode);
            //需要唤醒消费者信号量
            consumer.signal();
        } finally {
            //最终需要释放锁
            lock.unlock();
        }
    }

    /**
     * 消费者方法
     *
     * @return
     * @throws InterruptedException
     */
    public String consumer() throws InterruptedException {
        //加锁
        lock.lock();
        try {
//            如果当前不存在可用的产品code需要阻塞
            while (Objects.isNull(productCode)) {
                consumer.await();//阻塞
            }
            //产品被消费掉后,需要唤醒生产者
            producer.signal();
            return productCode;
        } finally {
            //TODO  消费掉数据(模拟)
            productCode = null;
            //最终释放锁
            lock.unlock();
        }
    }

}

第二部分:JDK中阻塞队列的实现方式.采用可重入锁和信号量进行控制

 

package com.milla.study.netbase.expert.concurrent.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Package: com.milla.study.netbase.expert.concurrent.lock
 * @Description: <阻塞队列>
 * @Author: MILLA
 * @CreateDate: 2020/6/1 18:14
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/6/1 18:14
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class ListBlockLockQueue {
    /**
     * 1.定义个队列
     * 2.采用可重入锁及信号量进行控制
     * 3.数据存储时,如果数据已经满了,则存储线程进行阻塞,唤醒获取数据线程
     * 4.数据获取时,如果当前数据已经消费完,则获取线程进行阻塞,获取存储数据线程
     * 5.数据的存储按照从0到队列的长度存入,如果存满则存储索引置0
     * 6.数据的获取按照从0到队列的长度获取,如果存满则存储索引置0
     */

    //定义队列容量
    private Integer size = 5;
    //采用集合定义一个队列
    private final Object[] data = new Object[size];

    //定义可重复锁
    ReentrantLock lock = new ReentrantLock();
    //获取为空信号量
    Condition take = lock.newCondition();
    //获取为满信号量
    Condition put = lock.newCondition();
    //获取数据索引值
    int takeIndex;
    //加入数据索引值
    int putIndex;

    //当前有多少个数据可取
    int count;

    /**
     * 拉取一个数据
     *
     * @return
     */
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            //没有数据可取-取数据线程阻塞
            while (count == 0) {
                take.await();//阻塞
            }
            //获取先存如数据
            Object o = data[takeIndex];
            //数据获取索引达到队列长度(说明当前数据取完了)-将获取索引置为0
            if (++takeIndex == data.length) {
                takeIndex = 0;
            }
            --count;
            System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
            put.signal();
            return o;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 存入数据
     *
     * @param obj
     */
    public void put(Object obj) throws InterruptedException {
        //先上锁
        lock.lock();
        try {
            //如果已经满了-阻塞
            while (count == data.length) {
                //就不能继续添加
                put.await();
            }
            //添加元素
            data[putIndex] = obj;
            //添加数据索引达到队列长度-将索引值重置为0
            if (++putIndex == data.length) {
                putIndex = 0;
            }
            ++count;
            System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
            take.signal();
        } finally {
            //最后释放锁
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ListBlockLockQueue queue = new ListBlockLockQueue();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                try {
                    queue.put("" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "一");
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    System.out.println(Thread.currentThread().getName() + ",获取数据: " + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "二");
        t2.start();
        t1.start();
        Thread.sleep(1000);
        queue.put("123456");
        Thread.sleep(1000);
        queue.put("00000");
        Thread.sleep(1000);
        queue.put("555555");

    }
}

 以上代码,均借鉴JDK 并发编程大神Doug Lea的示例代码,摘录如下:

 

 class BoundedBuffer {
    final Lock lock = new ReentrantLock();
               final Condition notFull  = lock.newCondition(); 
               final Condition notEmpty = lock.newCondition(); 
            
               final Object[] items = new Object[100];
    int putptr, takeptr, count;
 
    public void put(Object x) throws InterruptedException {
      lock.lock();
      try {
        while (count == items.length)
          notFull.await();
        items[putptr] = x;
        if (++putptr == items.length) putptr = 0;
        ++count;
        notEmpty.signal();
      } finally {
        lock.unlock();
      }
    }
 
    public Object take() throws InterruptedException {
      lock.lock();
      try {
        while (count == 0)
          notEmpty.await();
        Object x = items[takeptr];
        if (++takeptr == items.length) takeptr = 0;
        --count;
        notFull.signal();
        return x;
      } finally {
        lock.unlock();
      }
    }
  }

PS:最好的代码果然是底层的代码中,经过这么多年的千锤百炼自然是精益求精;路漫漫其修远兮,吾将上下而求索,革命尚未成功,同志尚需努力!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值