[笔记][Java7并发编程实战手册]2.5使用Lock实现同步二
来源:互联网 发布:vs2010读取excel数据 编辑:程序博客网 时间:2024/06/01 16:50
[笔记][Java7并发编程实战手册]系列目录
概要
接上一篇文章,练习修改锁的公平性,和在所中使用条件。
修改锁的公平性ReentrantLock
/** *构造一个锁对象,默认为非公平锁 */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
根据ReentrantLock的构造可以看出来,默认会构造非公平锁;
公平锁与非公平锁有什么区别
- 公平锁 :有多个线程并发访问公平锁的临界区,这些锁会等待上一个锁释放锁之后被公平策略选择一个线程获取锁(该策略就是在CLH选择一个等待时间最长的线程来访问临界区)
- 非公平锁:没有任何条件约束
- 公平和非公平策略只适用于lock() 和unlock()方法。而Lock接口的tryLock方法没有将线程置于休眠,fair属性并不影响这个方法。
详细解说请参考 Java多线程系列–“JUC锁”03之 公平锁(一)
示例演示公平策略
编写一个打印方法。里面有两个临界区,为了方便测试当前线程被执行后,第二个临界区等待锁的时间。才能看出效果
public class Client { public static void main(String[] args) throws InterruptedException { final FairSyncDemo fsd = new FairSyncDemo(); Thread[] arr = new Thread[10]; for (int i = 0;i < 10 ;i++){ arr[i] = new Thread(new Runnable() { @Override public void run() { fsd.print(); } }); } for (int i = 0;i < arr.length ;i++){ arr[i].start(); Thread.sleep(100); //让线程启动的时间增加。让公平锁来选择谁等待最久 } }}class FairSyncDemo { private ReentrantLock lock = new ReentrantLock(true); //构造公平锁 public void print(){ lock.lock(); Long time = (long)(Math.random() * 1000); System.out.println(Thread.currentThread().getName() + ",打印耗时" + time + "毫秒"); try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); lock.lock(); Long time2 = (long)(Math.random() * 1000); System.out.println(Thread.currentThread().getName() + ",stp2打印耗时" + time2 + "毫秒"); try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); }}
某一次的运行结果:
Thread-0,打印耗时821毫秒Thread-1,打印耗时23毫秒Thread-2,打印耗时19毫秒Thread-3,打印耗时455毫秒Thread-4,打印耗时951毫秒Thread-5,打印耗时857毫秒Thread-6,打印耗时680毫秒Thread-7,打印耗时36毫秒Thread-8,打印耗时116毫秒Thread-0,stp2打印耗时107毫秒Thread-1,stp2打印耗时960毫秒Thread-2,stp2打印耗时598毫秒Thread-9,打印耗时236毫秒Thread-3,stp2打印耗时969毫秒Thread-4,stp2打印耗时774毫秒Thread-5,stp2打印耗时657毫秒Thread-6,stp2打印耗时766毫秒Thread-7,stp2打印耗时83毫秒Thread-8,stp2打印耗时782毫秒Thread-9,stp2打印耗时745毫秒
结果说明:
我们启动十个线程的时候,每启动一个线程,睡眠100毫秒,
从Thread-0,打印耗时821毫秒 到 Thread-0,stp2打印耗时107毫秒,这段时间中。stp2等待的时间为线程1-8所运行的时间,等待时间大概是3000多毫秒了。(这里的一个策略我不太清楚)所以就被公平策略给选中了继续执行临界区代码。
下面的结果是非公平锁的运行结果:
修改为非公平锁: private ReentrantLock lock = new ReentrantLock(false); //构造非公平锁
Thread-0,打印耗时178毫秒Thread-0,stp2打印耗时492毫秒Thread-1,打印耗时534毫秒Thread-1,stp2打印耗时951毫秒Thread-2,打印耗时422毫秒Thread-2,stp2打印耗时882毫秒Thread-3,打印耗时353毫秒Thread-3,stp2打印耗时487毫秒Thread-4,打印耗时130毫秒Thread-4,stp2打印耗时74毫秒Thread-5,打印耗时900毫秒Thread-5,stp2打印耗时90毫秒Thread-6,打印耗时568毫秒Thread-7,打印耗时676毫秒Thread-7,stp2打印耗时220毫秒Thread-8,打印耗时511毫秒Thread-8,stp2打印耗时998毫秒Thread-9,打印耗时171毫秒Thread-9,stp2打印耗时425毫秒Thread-6,stp2打印耗时88毫秒
结果说明:
很明显能看出来公平与飞公平的效果相差有多大。
在锁中使用多条件condition
在lock中提供了与之关联的条件,一个锁可能关联一个或则多个条件,这些条件通过condition接口声明。目的是运行线程获取锁并且查看等待某一个条件是否满足,如果不满足则挂起直到某个线程唤醒它们。condition接口提供了挂起线程和唤起线程的机制;
示例演示条件的使用
ok,介绍下下面一堆代码所做的事情:使用了5个线程设置(生产)一个数值,使用5个线程移除(消费)这个数值;
在add 和 remoe中使用Condition来代替监视器锁的wait操作。和唤醒操作。
值得注意的是:
1. ConDemo 这个内部类修改成Con 启动抛出找不到类的错误异常(不知道con是关键字还是什么原因)
2. 读写线程数量如果不对等,将会出现死锁。
3. 所有的条件Condition必须使用对等的锁对象来创建lock.newCondition();
4. 条件必须用在lock() 和 unlock() 方法之间。
5. 在判定条件是否满足,需要在循环中判定,未满足条件的不能离开循环体,否则数据将得不到我们想要的结果
6. 调用await()方法进入休眠的线程可能会被中断,所以必须处理InterruptedException 异常
public class Client { public static void main(String[] args) throws InterruptedException { final ConDemo con = new ConDemo(); for (int i = 0; i < 5; i++) { new Thread(new Runnable() { @Override public void run() { con.add(); } }).start(); } for (int i = 0; i < 5; i++) { new Thread(new Runnable() { @Override public void run() { con.remove(); } }).start(); } }}class ConDemo { private ReentrantLock lock = new ReentrantLock(); //构造锁 private Long depot = null; //充当仓库,当有值的时候,会被remove走,并且设置为null。表示被取走了,等待add一个数 private Condition removeCon; private Condition addCon; public ConDemo() { this.removeCon = lock.newCondition(); this.addCon = lock.newCondition(); } /** * 设置 */ public void add() { lock.lock(); try { while (depot != null) { //有值,则等待 被取走。后再设置值 addCon.await(); //让当前线程操作等待 } depot = (long) (Math.random() * 1000); System.out.println(Thread.currentThread().getName() + ",----设置了:" + depot); //唤醒条件,让等待取走值的线程操作 removeCon.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } /** * 移除设置的数 */ public void remove() { lock.lock(); try { while (depot == null) { //没有值可取,则让条件等待 removeCon.await(); //让移除操作等待 } System.out.println(Thread.currentThread().getName() + ",拿走了:" + depot); depot = null; //唤醒条件,让等待设置值的线程。赶快设置值 addCon.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }}
某一次的运行结果:
Thread-2,----设置了:274Thread-8,拿走了:274Thread-1,----设置了:358Thread-5,拿走了:358Thread-0,----设置了:407Thread-7,拿走了:407Thread-3,----设置了:761Thread-9,拿走了:761Thread-4,----设置了:250Thread-6,拿走了:250
结果说明:可以看出以上的结果:有值了,才能被取走,被取走了,才能被生产一个值。
condition中的其他api
await(long time, TimeUnit unit)
boolean await(long time, TimeUnit unit) throws InterruptedException;
该方法直到以下情况之一之前,线程将一直处于休眠状态:
1. 其他某个线程中断当前线程
2. 其他某个线程调用了将当前线程挂起的条件的singal()或singalAll()方法
3. 指定的等待时间已经过去
awaitUninterruptibly()
它是不可中断的。这个线程将休眠直到其他某个线程调用了将他挂起的条件的singal()或则signalAll()方法。
awaitUntil(Date date)
直到发生以下情况之一之前,线程将一直处于休眠状态。
1. 其他某个线程中断当前线程
2. 其他某个线程调用了将他挂起的条件的dingal()或signalAll()方法
3. 指定的最后期限到了
也可以将条件与读写锁一起使用。
- [笔记][Java7并发编程实战手册]2.5使用Lock实现同步二
- [笔记][Java7并发编程实战手册]2.5使用Lock实现同步一
- [笔记][Java7并发编程实战手册]2.2使用syncronized实现同步方法
- [笔记][Java7并发编程实战手册]2.4在同步代码中使用条件-生产者与消费者
- [笔记][Java7并发编程实战手册]第三章-线程同步辅助类-3.1概要
- [笔记][Java7并发编程实战手册]3.5 在集合点的同步CyclicBarrier循环barrier
- [笔记][Java7并发编程实战手册]6.并发集合
- [笔记][Java7并发编程实战手册]7. 定制并发类
- [笔记][Java7并发编程实战手册]系列目录
- JAVA7并发编程手册笔记
- [笔记][Java7并发编程实战手册]3.2 资源的并发访问控制Semaphore信号量
- [笔记][Java7并发编程实战手册]3.3 资源的多副本并发访问控制Semaphore
- [笔记][Java7并发编程实战手册]3.6 并发阶段任务的运行phaser
- [笔记][Java7并发编程实战手册]3.7 并发阶段任务中的阶段切换phaser
- [笔记][Java7并发编程实战手册]3.8 并发任务间的数据交换Exchanger
- [笔记][Java7并发编程实战手册]系列第四章 4.1 简介
- [笔记][Java7并发编程实战手册]4.2 创建线程执行器newCachedThreadPool无界线程池
- [笔记][Java7并发编程实战手册]4.3 创建固定的线程执行器newFixedThreadPool线程池
- python中单继承和多继承中子类默认继承父类的哪个构造函数__init__
- [Leetcode]Add Binary
- SQL Server 2008下日志清理方法
- ButterKnife官方介绍
- NEU 1639: Small problem
- [笔记][Java7并发编程实战手册]2.5使用Lock实现同步二
- Codeforces Round #315 (Div. 2) B. Inventory (水题)
- hdu 3501
- Blog转移(暂时/永久)
- Java反射浅谈
- 山科大牛总结
- iOS开发之OC实例可见度,方法
- 在freemarker的模板文件中调用自定义的方法
- leetcode--83&82 Remove Duplicates from sorted list I&II