重入锁
来源:互联网 发布:python 里的key 编辑:程序博客网 时间:2024/04/18 09:25
重入锁
重入锁使用java.util.concurrent.locks.ReentrantLock
类来实现。其性能优于synchronized。ReentrantLock增加了一些高级功能级功能
- 等待可中断:在synchronized中,如果一个线程在等待锁,他只用两种结果,要么获得锁执行完,要么一直保持等待。可中断得等待是通知正在等待的线程,告诉他没必要再等待后。
- 实现公平锁:公平锁:会按照时间的先后顺序,保证先到先得。特点是它不会产生饥饿现象。而synchroized关键字进行所控制时,锁是非公平的。而重入锁可以设置为公平锁。 public ReetranLock(boolean fair) 当fair为true时,表示锁是公平的。实现公平锁必然要求系统维护一个有序队列,因此公平锁的成本比较高,性能也非常低向。默认情况下锁是非公平的。
- 绑定多个条件:类似于Object类的wait和notify方法,它是与ReentrantLock绑定的条件,可以绑定多个条件。
一个简单的例子:
import java.util.concurrent.locks.ReentrantLock;public class TestReentrantLock implements Runnable { public static ReentrantLock rlock = new ReentrantLock(); public static int i=0; @Override public void run(){ for(int j=0;j<1000000;j++){ rlock.lock(); try{ i++; }finally { rlock.unlock(); } } } public static void main(String args[]) throws InterruptedException { TestReentrantLock tl = new TestReentrantLock(); Thread t1 = new Thread(tl); Thread t2 = new Thread(tl); t1.start(); t2.start(); //表示当前线程等待t1执行完 t1.join(); t2.join(); System.out.println(i); }}
注意:退出临界区要释放锁,否则其他线程就没有机回访问临界区了。
这是文档中的说明:
为什么叫重入锁呢?
一个线程可以多次进入,当然必须多次释放锁。
rlock.lock();rlock.lock();try{ i++;}finally { rlock.unlock(); rlock.unlock(); //如果释放次数多,则回抛出java.lang.IllegalMonitorStateException异常 //rlock.unlock();}
中断响应
如果一个线程正在等待锁,那么它可以收到一个通知,被告知无序再等待,可以停止工作了。
lockInerruptible()的文档中解释为:
import java.util.concurrent.locks.ReentrantLock;public class Test2 implements Runnable{public static ReentrantLock lock1 = new ReentrantLock();public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test2(int lock){ this.lock = lock; } @Override public void run() { try{ if(lock==1){ lock1.lockInterruptibly(); System.out.println(Thread.currentThread().getName()+" get lock1"); try{ Thread.sleep(1000); }catch(Exception e){} lock2.lockInterruptibly(); System.out.println(Thread.currentThread().getName()+" get lock2"); }else{ lock2.lockInterruptibly(); System.out.println(Thread.currentThread().getName()+" get lock2"); try{ Thread.sleep(1000); }catch(InterruptedException e){} lock1.lockInterruptibly(); System.out.println(Thread.currentThread().getName()+" get lock1"); } }catch(InterruptedException e){ //System.out.println(e.getStackTrace()+"我是lockInterruptibly"); e.printStackTrace(); }finally { //判断当前线程是否拥有该锁 if(lock1.isHeldByCurrentThread()) lock1.unlock(); if(lock2.isHeldByCurrentThread()) lock2.unlock(); System.out.println(Thread.currentThread().getName()+" 退出!"); } } public static void main(String args[]) throws InterruptedException{ //这里new出两个实现Runnable的对象是因为为了传进去不同的lock值 Test2 t1 = new Test2(1); Test2 t2 = new Test2(2); Thread thread1 = new Thread(t1,"thread1"); Thread thread2 = new Thread(t2,"thread2"); thread1.start(); thread2.start(); Thread.sleep(1000); thread2.interrupt(); }}
执行过程是thread1占用lock1,休眠500毫秒,然后想占用lock2,与此同时,thread2占用lock2,休眠500毫秒后在请求kock1。可是当thread1,详情求lock2时,已经被thread2占用,因此只能进入阻塞状态,thread2也同理进入阻塞状态。因此进入死锁。但是这里使用了lockInterruptibly()方法。这是一个可以对中断进行响应的锁申请动作,即在等待锁的过程中可以响应中断。在thred2调用interrupt()方法,thread2线程被中断,thread2放弃对lock的申请,同时释放已获得的lock2,所以thread1可以得到lock2继续执行下去。
结果为:
thread2先中断,抛出异常,跳入finally块,释放资源,最终退出。
锁申请等待限时
如果给定一个等待时间,超过时间,让线程自动放弃。
import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock; public class Test4 implements Runnable { public static ReentrantLock lock = new ReentrantLock(); @Override public void run() { try { if(lock.tryLock(2,TimeUnit.SECONDS)){ Thread.sleep(5000); }else{ System.out.println(Thread.currentThread().getName()+" get lock failed"); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ if(lock.isHeldByCurrentThread()) lock.unlock(); } } public static void main(String args[]) { Test4 t = new Test4(); Thread thread1 = new Thread(t,"thread1"); Thread thread2 = new Thread(t,"thread2"); thread1.start(); thread2.start(); }}
tryLock()两个参数分别表示等待时长和计时单位,表示线程在请求锁的过程中,最多等待5秒,如果超过改时间则返回false,如果成果获得锁,则返回true。
该程序中首先任意一个线程先获得锁,然后休眠5秒,然而它一直占有锁,因此另一个线程无法再2秒内获得锁,因此失败。
tryLock()方法也可以不带参数,这种情况下,当前线程会尝试获得锁,如果锁未被其他线程占用,则申请锁会成功,把那个返回true,如果锁被其他线程占用,则当前线程不会等待,而是立即返回false。这种模式下不会引起线程等待,因此也不会产生死锁。
公平锁
大多数情况下,锁的申请都是非公平的,也就是说,线程1首先申请锁A,接着线程2页请求了锁A,当锁A可用时,线程1,2都有可能获得锁,系统知识在等待队列中随机挑选一个,因此不能保证公平性。所以有了公平锁,公平锁:会按照时间的先后顺序,保证先到先得。特点是它不会产生饥饿现象。而synchroized关键字进行所控制时,锁是非公平的。而重入锁可以设置为公平锁。
public ReetranLock(boolean fair)
当fair为true时,表示锁是公平的。实现公平锁必然要求系统维护一个有序队列,因此公平锁的成本比较高,性能也非常低向。默认情况下锁是非公平的。
import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;public class Test5 implements Runnable { public static ReentrantLock lock = new ReentrantLock(true); @Override public void run() { while(true) try { lock.lock(); System.out.println(Thread.currentThread().getName()+" get lock !"); }finally{ lock.unlock(); } } public static void main(String args[]) { Test5 t = new Test5(); Thread thread1 = new Thread(t,"thread1"); Thread thread2 = new Thread(t,"thread2"); thread1.start(); thread2.start(); }}
部分结果为:
可以看出两个线程基本上是交替获得锁。
2. Condition条件(搭配重入锁使用)
Condition类似于wait()和notify()的功能,它是与重入锁关联使用的。Lock接口中提供了newCondition()方法,该方法可以返回绑定到此Lock实例的心Condition实例。
方法解释:
await方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()或signalAll()方法时,线程会重新获得锁并继续执行,当线程被中断时,也能挑出等待。与Object的wait()方法相似。
singal()方法用于唤醒一个在等待中的线程。
注意:以上连个方法调用之前必须当前线程拥有锁。否则抛出IllegalMonitorStateException异常
import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class Test6 implements Runnable { public static ReentrantLock lock = new ReentrantLock(true); public static Condition condition = lock.newCondition(); @Override public void run() { try { lock.lock(); //当前线程释放锁,进入等待状态 condition.await(); System.out.println(System.currentTimeMillis()); System.out.println(Thread.currentThread().getName()+" get lock !"); } catch (InterruptedException e) { e.printStackTrace(); }finally{ lock.unlock(); } } public static void main(String args[]) throws InterruptedException { Test6 t = new Test6(); Thread thread1 = new Thread(t,"thread1"); thread1.start(); Thread.sleep(2000); //当thread1进入处于等待状态,main线程获得锁 lock.lock(); condition.signal(); System.out.println(System.currentTimeMillis()); Thread.sleep(2000); lock.unlock(); }}
结果为:
执行流程:
thread1线程调用await时,要求线程持有相关的重入锁,调用后,线程回释放这把锁,同理signal方法调用时,也要求线程先获得相关的锁,在signal方法调用后,系统会从当前的Condition对象的等待队列中唤醒一个线程,一旦线程唤醒,它会重新尝试获得之前绑定的锁,一旦成功获取await方法返回,继续执行。在调用signal后先睡眠2秒,并且保持了锁,释放了锁之后,await方法获取锁后才得以返回继续执行。因此打印出来的时间差为2000毫秒。
- 重入锁
- 重入锁
- 重入锁
- 重入锁
- 重入锁
- 重入锁--ReentrantLock
- 重入锁--ReentrantLock
- 重入锁ReentrantLock
- 重入锁ReentrantLock
- 重入锁--ReentrantLock
- 重入锁 ReentrantReadWriteLock
- 重入锁--ReentrantLock
- ReentrantLock 重入锁
- 重入锁ReentrantLock
- ReentrantLock重入锁
- 重入锁--ReentrantLock
- 重入锁ReentrantLock
- 重入锁概念
- 2017 Multi-University Training Contest
- 前端性能优化----yahoo前端性能团队总结的35条黄金定律
- Androidstudio SourceTree+码云管理代码
- 记一个实用的git命令---谁动了我的代码
- css Position 与 display
- 重入锁
- 鼠标移动上去 , 出现小手
- Mybatis和objectMapper的问题
- Python取日期范围循环操作的两种方法
- Invalid bound statement (not found):.....sysFunctionDao.getSysFunctionsById...<init>(MapperMethod.ja
- java学习是xml解析
- hdu6081 度度熊的王国战略(无向图全局最小割 stoer-wagner)
- vagrant provision 典型应用场景
- 神经网络入门(一)