Java多线程/并发21、利用Condition来实现阻塞队列
来源:互联网 发布:淘宝网板鞋 编辑:程序博客网 时间:2024/05/15 14:19
Java文档上的例子,利用Condition来实现阻塞队列。
假设有一个固定大小的缓冲区(缓冲区是队列,遵守FIFO),支持存和取方法。如果缓冲区为空时尝试取数据,那么线程将阻塞,直到缓冲区有一个可用的数据;如果缓冲区满了,这时尝试写数据,那么线程将被阻塞直到有可用空间。我们希望有两个独立的等待集(Condition阻塞队列),一个放置存数据的等待线程,一个放置取数据的等待线程,这样,当缓冲区有空位置的时侯,可以到“存数据的线程等待集”中唤醒一个线程存数据;当缓冲区从一无所有到有数据存入时,可以到“取数据的线程等待集”中唤醒一个线程取数据。这个关系不能搞错!
Condition是由Lock创建的,每个Lock锁可以创建多个Condition对象。只有同一把锁的Condition对象可以相互影响
public class ConditionDemo { static class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition();// 存数据的线程等待集——因为满了而等待 final Condition notEmpty = lock.newCondition();// 取数据的线程等待集——因为空了而等待 /* 用数组实现缓冲队列(FIFO) */ final Object[] items = new Object[5]; /* * putptr:存数据的索引位置 * takptr:取数据的索引位置 * count:当前队列大小 */ int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock();/* 给需要同步的代码加锁 */ try { /*如果数组满了,当前线程进入存数据的等待集。不用if是怕线程假醒*/ while (count == items.length) { System.err.println(Thread.currentThread().getName()+"写入数据["+putptr+"]异常:缓冲池已满"); notFull.await(); } /* 数据写入 */ System.out.println(Thread.currentThread().getName()+"写入数据["+putptr+"]:"+String.valueOf(x)); items[putptr] = x; /* 改变写入索引位置+1(为下次写入数据准备),如果超出数组边界,则重置为数组起始点 */ if (++putptr == items.length) putptr = 0; /* 增加数组有效果元素计数,即:队列大小加1 */ ++count; /* 至此队列中肯定有可用数据了,从“取数据的线程等待集”中唤醒线程 */ notEmpty.signal(); } finally { /* lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放 */ lock.unlock(); } } public Object take() throws InterruptedException { lock.lock();/* 给需要同步的代码加锁 */ try { /*如果数组为空,当前线程进入读数据的等待集。不用if是怕线程假醒*/ System.err.println("线程预备"); while (count == 0){ System.err.println(Thread.currentThread().getName()+"读取数据["+putptr+"]异常:缓冲池为空"); notEmpty.await(); } System.err.println("线程开始读数据"); /*读取数据*/ Object x = items[takeptr]; System.out.println(Thread.currentThread().getName()+"获取队列数据["+takeptr+"]:"+x); /* 改变读取索引位置+1(为下次读数据准备),如果超出数组边界,则重置为数组起始点 */ if (++takeptr == items.length) takeptr = 0; /* 减少数组有效果元素计数,即:队列大小减1 */ --count; /* 至此队列中肯定有可用空间了,从“存数据的线程等待集”中唤醒线程 */ notFull.signal(); return x; } finally { lock.unlock(); } } } public static void main(String[] args) { final BoundedBuffer boundbuffe =new BoundedBuffer(); for(int i=0;i<10;i++){ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(3000)); int temp=new Random().nextInt(1000); boundbuffe.put(temp); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } for(int i=0;i<5;i++){ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(500)); boundbuffe.take(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }}
例子不难理解,我用中文注释了一下。调整存、取线程的开启数量和sleep时间,可以轻松制造“写数据异常”
运行产生的结果(部分截图):
想到一个问题,condition的await()方法让一个线程等待后,等到这个线程被唤醒时,它是接着往下执行呢?还是refresh,重新执行整个方法或lock块呢?
为了求证,在take()方法内用两个输出包围notEmpty.await()代码块,变成:
/*如果数组为空,当前线程进入读数据的等待集不用if是怕线程假醒*/System.out.println(Thread.currentThread().getName()+"线程预备");while (count == 0){ System.err.println(Thread.currentThread().getName()+"读取数据["+putptr+"]异常:缓冲池为空"); notEmpty.await();}System.out.println(Thread.currentThread().getName()+"线程开始读数据");
重新运行结果:
“线程预备”这句话只出现了一次,表明如果线程await()后被其它线程重新唤醒后,是继续往下运行。
0 0
- Java多线程/并发21、利用Condition来实现阻塞队列
- java多线程:10、阻塞队列【Condition】
- java 使用ReentrantLock Condition实现阻塞队列
- 聊聊高并发(十四)理解Java中的管程,条件队列,Condition以及实现一个阻塞队列
- Java多线程 阻塞队列和并发集合
- Java多线程阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- 【Java基础】并发 - 多线程 - 阻塞队列
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java多线程/并发26、阻塞队列BlockingQueue
- java实现缓冲区(阻塞队列,condition,lock)
- 二叉树的实现及相关函数的实现
- SAP HANA CREATE PROCEDURE
- opencv笔记(8):Haar特征
- 3Sum Closest
- 全兼容系列(3)-------------------鼠标滑过
- Java多线程/并发21、利用Condition来实现阻塞队列
- Shell函数的7种用法介绍 ---待整理
- 南阳理工acm61 传纸条 双线dp
- Linux Kernel 学习笔记8:同步与互斥之信号量
- JS load事件(UI)
- 使用leaflet或者openlayers 3 调用MapServer服务最佳实践完整说明
- BZOJ1012
- OSTaskCreateHook()
- Thinkphp 多库查询