一、ReentrantLock简介
ReentrantLock字面意义上理解为可重入锁。那么怎么理解可重入这个概念呢?或者说和我们经常用的synchronized又什么区别呢?
ReentrantLock可重入锁是一种递归无阻塞的同步锁机制,简单意思就是说可重入锁就是当前持有该锁的线程能够多次获取该锁,无需等待。它可以等同于synchronized的作用,但是它比synchronized更加的灵活,更加的强大,能够更好的降低死锁发生的概率。
二、ReentrantLock使用和Lock接口中的API介绍
2.1、构造方法
ReentrantLock是lock接口的实现类。
无参数构造。直接返回ReentrantLock锁的实例。
有参数构造。支持传入一个boolean类型的参数表示这个锁是公平锁还是非公平锁。ReentrantLock默认是非公平锁。设置参数为true表示为公平锁。
2.2、lock()方法
lock方法表示当前线程去获取锁。不能获取到锁则等待。需要注意的是ReentrantLock锁需要我们手动的释放锁,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
1 ReentrantLock lock = new ReentrantLock(); 2 lock.lock();//获取锁 3 try{ 4 //处理业务 5 }catch (Exception e){ 6 7 }finally { 8 lock.unlock();//释放锁 9 }
2.3、tryLock()方法
由于lock方法获取锁以后只能一直等待。这样有些不太友好。提供了tryLock方法。该方法会提供一个boolean返回值。为true表示获取锁成功,为false则表示获取锁失败了。这样可以避免一直无脑的等待锁。同时tryLock提供了一个重载方法tryLock(long time, TimeUnit unit)。tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
1 if (lock.tryLock()){ 2 try { 3 //得到锁 处理业务 4 } catch(Exception e){ 5 6 } finally { 7 lock.unlock(); 8 } 9 }else{ 10 //没有获取到锁,先执行其他的任务 11 }
2.4、lockInterruptibly()方法。响应中断操作
当通过这个锁去获取锁的时候可以响应的中断操作,意思就是当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。这个过程叫做中断自己,会捕获到InterruptedException异常。注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。
三、ReentrantLock类简单使用
前面有说了该类主要是更加灵活的使用synchronized同步锁。
1 import java.util.ArrayList; 2 import java.util.List; 3 import java.util.concurrent.locks.ReentrantLock; 4 5 public class TestReentrantLock { 6 public static List<Integer> list = new ArrayList<>(); 7 public ReentrantLock lock = new ReentrantLock(); 8 public static void main(String[] args) { 9 TestReentrantLock addThread = new TestReentrantLock(); 10 new Thread(){ 11 public void run(){ 12 //addThread.insert(Thread.currentThread()); 13 addThread.tryInsert(Thread.currentThread()); 14 } 15 }.start(); 16 17 new Thread(){ 18 public void run(){ 19 //addThread.insert(Thread.currentThread()); 20 addThread.tryInsert(Thread.currentThread()); 21 } 22 }.start(); 23 } 24 25 /** 26 * 只有一个线程能够对list集合进行增加操作 27 */ 28 public void insert(Thread thread){ 29 30 lock.lock(); 31 try{ 32 System.out.println("当前线程:"+thread.getName()+"获取了锁"); 33 for (int i = 0;i < 10;i++){ 34 list.add(i); 35 } 36 }catch (Exception e){ 37 38 }finally { 39 System.out.println("当前线程:"+thread.getName()+"释放了锁"); 40 lock.unlock(); 41 } 42 43 } 44 45 /** 46 * 尝试插入数据。如果当前不让插入数据。则去做其他业务 47 * @param thread 48 */ 49 public void tryInsert(Thread thread){ 50 if (lock.tryLock()){ 51 try{ 52 System.out.println("当前线程:"+thread.getName()+"获取了锁"); 53 for (int i = 0;i < 10;i++){ 54 list.add(i); 55 } 56 }catch (Exception e){ 57 58 }finally { 59 System.out.println("当前线程:"+thread.getName()+"释放了锁"); 60 lock.unlock(); 61 } 62 }else{ 63 System.out.println("线程"+thread.getName()+"获取锁失败"); 64 } 65 } 66 67 68 }
线程Thread-1获取锁失败 当前线程:Thread-0获取了锁 当前线程:Thread-0释放了锁
四、(Lock接口)ReentrantLock和synchronized的一些比较
4.1、Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现。
4.2、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。
4.3、Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。
4.4、通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。