概述
1.ReentrantLock简介
2.ReentrantLock示例
3.ReentrantLock与Condition示例
ReentrantLock简介
ReentrantLock是一个可重入的互斥锁,又称独占锁;
即ReentrantLock是同一时间点只能被一个线程持有,可重入的意思就是持有该锁的线程可再次获取锁。
ReentrantLock分为公平和非公平锁。它们的区别体现在获取锁的机制上是否公平。锁是为了保护竞争资源,防止多个线程同时操作而出错。ReentrantLock在同一时间只能被一个线程获取,在某个线程获取时,其他线程就必须等待。ReentrantLock是通过一个FIFO的队列来管理获取该锁的线程的,在公平锁的情况下,依次排队获取;在非公平锁的情况下,在锁是可获取状态时,不管是不是队列的开头都会获取锁。
ReentrantLock示例
还是以生产者消费者的场景来举例,接下来通过两个示例还对比下锁存在和不存在的情况:
示例1:无锁的情况,生产者往仓库生成,消费者从仓库消费,操作完后,sleep一小会;
//仓库的代码
public class Depot { private int size; private int maxSize=50; private Condition prodCondition; private Lock lock; public Depot(){ this.size=0; this.lock=new ReentrantLock(); } public void prod(int val){ try{ size+=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" produce "+val+" total size is "+size); } } public void consum(int val){ try{ size-=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" consumer "+val+" total size is "+size); } } }
测试代码:
public class ReentrantLockTest { public static void main(String[] args){
//测试生产者消费者 Depot depot=new Depot(); new ProducerLock(depot,10).start(); new ConsumerLock(depot,20).start(); new ProducerLock(depot,30).start(); new ProducerLock(depot,20).start(); new ProducerLock(depot,20).start(); new ConsumerLock(depot,70).start(); } } class ProducerLock extends Thread{ Depot depot; int val; public ProducerLock(Depot depot,int val){ this.depot=depot; this.val=val; } public void run(){ depot.prod(val); } } class ConsumerLock extends Thread{ Depot depot; int val; public ConsumerLock(Depot depot,int val){ this.depot=depot; this.val=val; } public void run(){ depot.consum(val); } }
输出结果: 可以看到多个线程对同一仓库进行操作,结果完全乱套了
Thread-0 produce 10 total size is -10 Thread-3 produce 20 total size is -10 Thread-4 produce 20 total size is -10 Thread-1 consumer 20 total size is -10 Thread-2 produce 30 total size is -10 Thread-5 consumer 70 total size is -10
示例2,现在看下加锁后的情况,即在Depot类中将prod和consum方法里面的操作分别加上lock,以及在finally里释放lock,如下:
public class Depot { private int size; private int maxSize=50; private Lock lock; public Depot(){ this.size=0; this.lock=new ReentrantLock(); } public void prod(int val){ lock.lock(); try{ size+=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" produce "+val+" total size is "+size); }finally { lock.unlock(); } } public void consum(int val){ lock.lock(); try{ size-=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" consumer "+val+" total size is "+size); }finally { lock.unlock(); } } }
输出结果如下:可以看到多个线程进行操作时,没有出现混乱的情况,但是结果还是有负数,正常来讲仓库满了就不生成了,仓库为空就不让消费了,怎么解决呢?
结合Condition来解决
Thread-0 produce 10 total size is 10 Thread-1 consumer 20 total size is -10 Thread-2 produce 30 total size is 20 Thread-3 produce 20 total size is 40 Thread-4 produce 20 total size is 60 Thread-5 consumer 70 total size is -10
ReentrantLock与Condition示例
为解决上面的问题,在lock中引入Condition,设置两个Condition,生成者在仓库满时,进入等待状态,同时唤醒消费者线程,消费者在仓库为空时,进入等待。同时唤醒生产者线程。
public class Depot { private int size; private int maxSize=50; // private int count; private Condition prodCondition; private Condition consumCondition; private Lock lock; public Depot(){ this.size=0; this.lock=new ReentrantLock(); this.prodCondition=this.lock.newCondition(); this.consumCondition=this.lock.newCondition(); } public void prod(int val){ lock.lock(); try{ while(size+val>maxSize){ //如果生产超过max值,则生产者进入等待 try { prodCondition.await(); System.out.println(Thread.currentThread().getName()+" is awaiting"); } catch (Exception e) { e.printStackTrace(); } } size+=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" produce "+val+" total size is "+size); consumCondition.signal(); //唤醒消费者线程 }finally { lock.unlock(); } } public void consum(int val){ lock.lock(); try{ while(size-val<0){ //如果当前大小减去要消费的值,如果小于0的话,则进入等待 try { consumCondition.await(); System.out.println(Thread.currentThread().getName()+" is awaiting"); } catch (Exception e) { e.printStackTrace(); } } size-=val; try { Thread.sleep((long)val); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" consumer "+val+" total size is "+size); prodCondition.signal(); //唤醒生产者线程 }finally { lock.unlock(); } } }
测试结果如下:可以看到没有负数的情况,也没有仓库大小超过50的情况,还能看到线程进入等待的情况。
Thread-0 produce 10 total size is 10
Thread-2 produce 30 total size is 40
Thread-1 consumer is awaiting
Thread-1 consumer 20 total size is 20
Thread-3 producer is awaiting
Thread-3 produce 20 total size is 40
Thread-5 consumer is awaiting
这里只讲解了ReentrantLock和condition的使用,原理部分会另开一偏文章进行讲解。