java多线程(3):Lock接口和Condition监视器接口使用详解
来源:互联网 发布:网络有初心 编辑:程序博客网 时间:2024/05/29 14:54
前言
前面使用的synchronized关键字来保证同步时,每个锁对象都有自己的一套监视器方法(wait,notify,notifyAll),这些方法是继承自Object类的。也就是说,锁对象被实例化后,只有一套监视器方法,一个线程要唤醒其他线程只能使用notifyAll方法,这也是多线程中的效率问题的根源。
为此,jdk1.5引入了java.util.concurrent.locks包,并提供了Lock和Condition接口及实现类。下面来看这两个接口的jdk文档。
- Lock 实现提供了比使用 synchronized方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
- Condition 将 Object 监视器方法(wait、notify 和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待set(wait-set)。其中,Lock 替代了synchronized 方法和语句的使用,Condition 替代了 Object监视器方法的使用。
这两个接口将“锁对象”和“监视器对象”分开,这样,一个“锁”就可以关联多个“监视器对象”。也就能实现生产者线程固定唤醒消费者线程,消费者线程固定唤醒生产者线程。
正文
一,Lock和Condition接口的组合使用
我们来修改上一篇中的多生产者和多消费者代码。
package com.jimmy.ThreadCommunication;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class Resource3{ // 资源函数 private String productName; // 共享资源不变 private int count = 1; private Lock lock = new ReentrantLock(); // 定义一个锁对象 private Condition conProducer = lock.newCondition(); // 获得lock锁的"生产者"线程监视器对象 private Condition conConsumer = lock.newCondition(); // 获得lock锁的"消费者"线程监视器对象 private boolean flag = false; // 资源类增加一个标志位,默认false,也就是没有资源 public void produce(String name){ lock.lock(); // 获取锁 try { // 业务代码要写在try块中 while (flag == true) { // flag判断不变,即如果flag为true,也就是有资源了,生产者线程就去等待。 try { conProducer.await(); // "生产者"线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } this.productName = name + count; count ++; System.out.println(Thread.currentThread().getName()+"....生产者.."+this.productName); flag = true; // 生产完了就将flag修改为true conConsumer.signal(); // 随机唤醒"消费者"线程中被await的一个线程 } finally { lock.unlock(); // 无论如何都要释放锁 } } public void consume() { lock.lock(); try { while (flag == false) { try { conConsumer.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"...消费者.."+this.productName); flag = false; // 消费完了就把标志改为false conProducer.signal(); // 随机唤醒"生产者"线程中被await的一个线程 } finally { lock.unlock(); // 无论如何,最后要释放锁 } }}class Producer3 implements Runnable{ // 生产者类不变 private Resource3 res; //生产者初始化就要分配资源 public Producer3(Resource3 res) { this.res = res; } @Override public void run() { for (int i = 0; i < 5; i++) { res.produce("bread"); } }}class Comsumer3 implements Runnable{ // 消费者类不变 private Resource3 res; //同理,消费者一初始化也要分配资源 public Comsumer3(Resource3 res) { this.res = res; } @Override public void run() { for (int i = 0; i < 10; i++) { res.consume(); } }}public class ProducerAndConsumer3 { public static void main(String[] args) { // 测试程序不变 Resource3 resource = new Resource3(); // 实例化资源 Producer3 producer = new Producer3(resource); // 实例化生产者,并传入资源对象 Comsumer3 comsumer = new Comsumer3(resource); // 实例化消费者,并传入相同的资源对象 Thread threadProducer1 = new Thread(producer); // 创建2个生产者线程 Thread threadProducer2 = new Thread(producer); Thread threadComsumer1 = new Thread(comsumer); // 创建2个消费者线程 Thread threadComsumer2 = new Thread(comsumer); threadProducer1.start(); threadProducer2.start(); threadComsumer1.start(); threadComsumer2.start(); }}
代码与上一篇博客中相比,Lock.lock()和Lock.unlock()代替了“synchronized”关键字,而且Lock的使用更加灵活,也更加“面向对象”。Condition.await()/Condition.signal()/Condition.signalAll()方法替代了以前的this.await()/this.signal()/this.signalAll()方法,更重要的是,Lock和Condition分开,而且一个Lock可以绑定多个Condition,这就为唤醒对方线程打下了基础,从而提高了多线程的执行效率。
二,多资源的情况
前面写的代码都是多个线程对单一资源的操作,实际中资源会放在一个集合中,生产者不停的往里面放,消费者不停的从里面取,下面就是将资源放进数组中循环存取的示例代码。
package com.jimmy.ThreadCommunication;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class BoundedBuffer { final Lock lock = new ReentrantLock(); // 获得"锁"对象 final Condition notFull = lock.newCondition(); // 得到锁对象的监视器对象 final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[50]; // 资源放进数组中循环存取 int putptr; // 生产者脚标 int takeptr; // 消费者脚标 int 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; System.out.println(Thread.currentThread().getName()+"...生产..No "+putptr+"..剩余 "+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; System.out.println(Thread.currentThread().getName()+"..消费..No "+takeptr+"..剩余 "+count); notFull.signal(); return x; } finally { lock.unlock(); } }}class Producer4 implements Runnable{ private BoundedBuffer buffer; public Producer4(BoundedBuffer buffer) { this.buffer = buffer; } @Override public void run() { int i = 0; while(i < 200) { try { buffer.put("bread"); i++; } catch (InterruptedException e) { e.printStackTrace(); } } }}class Consumer4 implements Runnable{ private BoundedBuffer buffer; public Consumer4(BoundedBuffer buffer) { this.buffer = buffer; } @Override public void run() { int i = 0; while(i < 200) { try { buffer.take(); i++; } catch (InterruptedException e) { e.printStackTrace(); } } }}public class ProducerAndConsumer4 { public static void main(String[] args) { BoundedBuffer boundedBuffer = new BoundedBuffer(); Producer4 producer4 = new Producer4(boundedBuffer); Consumer4 consumer4 = new Consumer4(boundedBuffer); Thread produceThread1 = new Thread(producer4); Thread produceThread2 = new Thread(producer4); Thread consumeThread1 = new Thread(consumer4); Thread consumeThread2 = new Thread(consumer4); produceThread1.start(); produceThread2.start(); consumeThread1.start(); consumeThread2.start(); }}
来看一下输出的结果,只截取了其中一部分:
Thread-1...生产..No 1..剩余 1Thread-1...生产..No 2..剩余 2Thread-1...生产..No 3..剩余 3Thread-1...生产..No 4..剩余 4Thread-1...生产..No 5..剩余 5Thread-1...生产..No 6..剩余 6Thread-1...生产..No 7..剩余 7Thread-1...生产..No 8..剩余 8Thread-1...生产..No 9..剩余 9Thread-1...生产..No 10..剩余 10Thread-1...生产..No 11..剩余 11Thread-1...生产..No 12..剩余 12Thread-1...生产..No 13..剩余 13Thread-1...生产..No 14..剩余 14Thread-1...生产..No 15..剩余 15Thread-1...生产..No 16..剩余 16Thread-2..消费..No 1..剩余 15Thread-2..消费..No 2..剩余 14Thread-2..消费..No 3..剩余 13Thread-2..消费..No 4..剩余 12Thread-2..消费..No 5..剩余 11Thread-2..消费..No 6..剩余 10Thread-2..消费..No 7..剩余 9Thread-2..消费..No 8..剩余 8Thread-2..消费..No 9..剩余 7Thread-2..消费..No 10..剩余 6Thread-2..消费..No 11..剩余 5Thread-2..消费..No 12..剩余 4Thread-2..消费..No 13..剩余 3Thread-2..消费..No 14..剩余 2Thread-2..消费..No 15..剩余 1Thread-2..消费..No 16..剩余 0Thread-0...生产..No 17..剩余 1Thread-0...生产..No 18..剩余 2Thread-0...生产..No 19..剩余 3Thread-0...生产..No 0..剩余 4Thread-0...生产..No 1..剩余 5Thread-0...生产..No 2..剩余 6Thread-0...生产..No 3..剩余 7Thread-0...生产..No 4..剩余 8Thread-0...生产..No 5..剩余 9Thread-0...生产..No 6..剩余 10Thread-0...生产..No 7..剩余 11Thread-0...生产..No 8..剩余 12Thread-0...生产..No 9..剩余 13Thread-0...生产..No 10..剩余 14Thread-0...生产..No 11..剩余 15Thread-0...生产..No 12..剩余 16Thread-0...生产..No 13..剩余 17Thread-0...生产..No 14..剩余 18Thread-0...生产..No 15..剩余 19Thread-0...生产..No 16..剩余 20
我们来看,生产者线程Thread-1首先得到执行权,然后往能容纳20个资源对象的数组中存数据,存到第16个后,被消费者线程Thraed-2获得执行权,它从第一个资源开始取,一直取到第16个,此时数组中的资源已经空了,就通知生产者线程继续生产,生产者会接着上次停留的地方(也就是脚标17)继续生产,到头了就又从头开始生产。循环往复。
总结
Lock和Condition的组合使用,使“锁对象”和锁的“监视器对象”分离,这样既操作灵活,又可以直接唤醒对方线程,提高多线程的效率。
- java多线程(3):Lock接口和Condition监视器接口使用详解
- java多线程-Lock与Condition接口的使用详解
- java 线程通信Lock和condition接口
- Java多线程--Condition接口
- Java多线程Condition接口原理详解
- java 多线程Condition接口的使用
- java多线程学习笔记(七) ——消费者与生产者(LOCK、Condition接口)
- Java 多线程:Lock接口
- Java多线程--Lock接口
- Java多线程之~~~Lock接口和ReentrantLock的使用
- Java 多线程4:Condition 接口
- Java——jdk1.5多线程 Lock接口及Condition接口
- java 多线程 lock接口 的使用
- Java多线程开发之~~~多条件Condition接口的使用
- (9)Java多线程之Lock接口
- JAVA多线程顺序执行(使用join,lock,condition,信号量)原理和java源代码
- JAVA多线程-Lock的使用(一)-ReentrantLock与Condition
- Java多线程 之 lock与condition的使用(十四)
- ubuntu无图形界面,只有壁纸
- mysql慢查询日记
- C/C++中extern关键字详解
- Linux下的SVN服务器搭建
- VC/MFC如何设置对话框背景颜色
- java多线程(3):Lock接口和Condition监视器接口使用详解
- Win_Server_2003-2016_加密勒索事件必打补丁合集
- 总线、设备和驱动的关系
- 初识 nodejs
- android log丢失(三)动态切换logd机制和kernel机制
- C#中的()=>
- SAE数据库完美备份加强版
- 一个用粒子动画显示文字的 Android 自定义 View
- 阿里云将增设马来西亚数据中心_纳吉布总理大赞中国技术