java concurrent系列3---ReentrantLock
来源:互联网 发布:ghost软件哪个好 编辑:程序博客网 时间:2024/05/17 04:24
ReentrantLock 一个可重入的互斥锁,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。
ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。
可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。
此类的构造方法接受一个可选的公平参数。当设置为 true时,在多个线程的争用下,这些锁定倾向于将访问权授予等待时间最长的线程。否则此锁定将无法保证任何特定访问顺序。
package concurrent;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.ReentrantLock;public class MyReentrantLock extends Thread{TestReentrantLock lock;private int id;public MyReentrantLock(int i,TestReentrantLock test){ this.id=i; this.lock=test;}public void run(){ lock.print(id);}public static void main(String args[]){ ExecutorService service=Executors.newCachedThreadPool(); TestReentrantLock lock=new TestReentrantLock(); for(int i=0;i<10;i++){ service.submit(new MyReentrantLock(i,lock)); } service.shutdown();}}class TestReentrantLock{private ReentrantLock lock=new ReentrantLock();public void print(int str){ try{ lock.lock(); System.out.println(str+"获得"); Thread.sleep((int)(Math.random()*1000)); } catch(Exception e){ e.printStackTrace(); } finally{ System.out.println(str+"释放"); lock.unlock(); }}}
此处的ReentrantLock 与synchronized用法相同。
synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。
1.某个线程在等待一个锁的控制权的这段时间需要中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候
下面细细道来……
先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)
这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:
public class Buffer { private Object lock; public Buffer() { lock = this; } public void write() { synchronized (lock) { long startTime = System.currentTimeMillis(); System.out.println("开始往这个buff写入数据…"); for (;;)// 模拟要处理很长时间 { if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) break; } System.out.println("终于写完了"); } } public void read() { synchronized (lock) { System.out.println("从这个buff读数据"); } } }
接着,我们来定义2个线程,一个线程去写,一个线程去读。
public class Writer extends Thread { private Buffer buff; public Writer(Buffer buff) { this.buff = buff; } @Override public void run() { buff.write(); } } public class Reader extends Thread { private Buffer buff; public Reader(Buffer buff) { this.buff = buff; } @Override public void run() { buff.read();//这里估计会一直阻塞 System.out.println("读结束"); } }
好了,写一个Main来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。
public class Test { public static void main(String[] args) { Buffer buff = new Buffer(); final Writer writer = new Writer(buff); final Reader reader = new Reader(buff); writer.start(); reader.start(); new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); for (;;) { //等5秒钟去中断读 if (System.currentTimeMillis() - start > 5000) { System.out.println("不等了,尝试中断"); reader.interrupt(); break; } } } }).start(); } }
我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成 T_T ,即使我们中断它,它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。
import java.util.concurrent.locks.ReentrantLock; public class BufferInterruptibly { private ReentrantLock lock = new ReentrantLock(); public void write() { lock.lock(); try { long startTime = System.currentTimeMillis(); System.out.println("开始往这个buff写入数据…"); for (;;)// 模拟要处理很长时间 { if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) break; } System.out.println("终于写完了"); } finally { lock.unlock(); } } public void read() throws InterruptedException { lock.lockInterruptibly();// 注意这里,可以响应中断 try { System.out.println("从这个buff读数据"); } finally { lock.unlock(); } } }
当然,要对reader和writer做响应的修改
public class Reader extends Thread { private BufferInterruptibly buff; public Reader(BufferInterruptibly buff) { this.buff = buff; } @Override public void run() { try { buff.read();//可以收到中断的异常,从而有效退出 } catch (InterruptedException e) { System.out.println("我不读了"); } System.out.println("读结束"); } } /** * Writer倒不用怎么改动 */ public class Writer extends Thread { private BufferInterruptibly buff; public Writer(BufferInterruptibly buff) { this.buff = buff; } @Override public void run() { buff.write(); } } public class Test { public static void main(String[] args) { BufferInterruptibly buff = new BufferInterruptibly(); final Writer writer = new Writer(buff); final Reader reader = new Reader(buff); writer.start(); reader.start(); new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); for (;;) { if (System.currentTimeMillis() - start > 5000) { System.out.println("不等了,尝试中断"); reader.interrupt(); break; } } } }).start(); } }
这次“读”线程接收到了lock.lockInterruptibly()中断,并且有效处理了这个“异常”。好奇的读者,肯定要探个究竟,为什么ReentrantLock能做到这点,接下来,我们去迷宫探险吧……
- java concurrent系列3---ReentrantLock
- java concurrent之ReentrantLock
- java.util.concurrent.ReentrantLock
- Java Concurrent Lock ReentrantLock简介
- java.util.concurrent.locks.ReentrantLock
- Java.concurrent.locks(2)-ReentrantLock
- java.util.concurrent.locks.ReentrantLock 源码剖析
- java.util.concurrent 之 可重入锁ReentrantLock
- java.util.concurrent.locks.ReentrantLock重入锁源码解析
- concurrent包之reentrantlock
- java concurrent系列2---BlockingQueue
- (译)Java Concurrent系列--ExecutorService
- (译)Java Concurrent系列--ArrayBlockingQueue
- (译)Java Concurrent系列--BlockingQueue
- (译)Java Concurrent系列--DelayQueue
- java并发系列:深入分析ReentrantLock
- concurrent-4-AQS-ReentrantLock-Lock
- java concurrent系列1---Java并发处理
- hive创建分区表问题
- 如何用回车键提交【无表单数据】?
- Android java层和C层的相互调用
- qvfb中不能显示中文
- 日志打印到屏幕同时保存到文件及获取管道返回状态
- java concurrent系列3---ReentrantLock
- eclipse插件开发常用的选择器
- 鸟哥的Linux学习笔记
- Ubuntu 10.10/11.10下安装与配置Squid代理软件
- 数据库行列互换
- errno — Standard errno system symbols
- vs2008 MFC 常见错误--新员工常犯的错误!
- Dwr级联的小例子
- Oracle Hints 提示