ReentrantLock 是什么
重入锁,是一个可以在持有相同锁的执行代码之间进行互斥的锁。具有与使用synchronized 方法和语句时同样的基本行为和语义,但是功能更多,也更加灵活。一个ReentrantLock 由一个线程调用lock方法获取,如果锁没有被另外一个线程锁定。当前线程成功将锁获取并锁定,且没有解锁,那么方法将返回true。如果当前线程已经获取到该锁,调用lock方法,将立即返回true。
ReentrantLock 跟synchronized 区别
两者都是阻塞式同步锁,当一个线程占用锁后,其他线程无法进入被锁定的代码块。区别是:ReentrantLock 在使用的时候,必须创建lock对象。必须在开始部分写lock语句,结束部分写unlock语句。如果没有手工释放,会导致死锁。同样,ReentrantLock的锁粒度要比synchronized要细,也更加灵活。
ReentrantLock 基本方法
lock-unLock,基本的加锁解锁实现,如果锁没有被另外一个线程占用则立即返回true,如果当前线程本身已经持有该锁定,则保持计数增加1,返回true。如果锁被其他线程保持,则当前线程阻塞,在获取到 锁之前一直处于休眠状态。代码如下:
1 Lock lock = new ReentrantLock(); 2 3 void m1() { 4 try { 5 lock.lock(); 6 for (int i = 0; i < 10; i++) { 7 TimeUnit.SECONDS.sleep(1); 8 System.out.println(i); 9 } 10 } catch (Exception e) { 11 e.printStackTrace(); 12 }finally { 13 lock.unlock(); 14 } 15 } 16 17 void m2() { 18 try { 19 lock.lock(); 20 System.out.println("m2....."); 21 } catch (Exception e) { 22 e.printStackTrace(); 23 }finally { 24 lock.unlock(); 25 } 26 } 27 28 public static void main(String[] args) { 29 T02_ReentrantLock2 rl = new T02_ReentrantLock2(); 30 new Thread(rl::m1).start(); 31 try { 32 TimeUnit.SECONDS.sleep(1); 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } 36 new Thread(rl::m2).start(); 37 } 38
trylock,当lock方法执行的时候,如果获取不到锁,线程就会阻塞。trylock方法可以在不阻塞线程的情况下,尝试获取锁。如果没有被其他线程保持锁定,那么当前线程获取锁定,并返回true。如果锁由另一个线程持有,则该方法将立即返回值为false 。trylock方法还有一个多态的方法,可以传入一个超时时间,在超时结束之前未获取到锁,将会等待。超时结束仍然未获取到锁,将会返回false。代码如下:
1 Lock lock = new ReentrantLock(); 2 3 void m1() { 4 try { 5 lock.lock(); 6 for (int i = 0; i < 10; i++) { 7 TimeUnit.SECONDS.sleep(1); 8 System.out.println(i); 9 } 10 } catch (Exception e) { 11 e.printStackTrace(); 12 } finally { 13 lock.unlock(); 14 } 15 } 16 17 /** 18 * 可以使用trylock对代码进行尝试锁定,不管锁定与否,方法都将继续执行, 19 * 可以根据tryLock的返回值来确定锁定,也可以指定tryLock的时间,超时后抛出异常, 20 */ 21 void m2() { 22 /* 23 * boolean locked = lock.tryLock(); 24 * System.out.println("m2....."+locked); 25 * if(locked) locked.unlock(); 26 */ 27 boolean locked = false; 28 29 try { 30 locked = lock.tryLock(5,TimeUnit.SECONDS); 31 32 System.out.println("m2....."+locked); 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } finally { 36 if(locked)lock.unlock(); 37 } 38 } 39 40 public static void main(String[] args) { 41 T02_ReentrantLock3 rl = new T02_ReentrantLock3(); 42 new Thread(rl::m1).start(); 43 try { 44 TimeUnit.SECONDS.sleep(1); 45 } catch (Exception e) { 46 e.printStackTrace(); 47 } 48 new Thread(rl::m2).start(); 49 }
lockInterruptibly
public static void main(String[] args) { Lock lock = new ReentrantLock(); Thread t1 = new Thread(() -> { lock.lock(); try { System.out.println("t1开始执行"); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); System.out.println("t1执行结束"); } catch (InterruptedException e) { System.out.println("interruped"); e.printStackTrace(); } finally { lock.unlock(); } }); t1.start(); Thread t2 = new Thread(() -> { System.out.println("t2 开始执行"); boolean isLock =false; try { try { lock.lockInterruptibly(); // 可以对interrup()方法做出响应 isLock = true; } catch (Exception e) { e.printStackTrace(); isLock = false; } System.out.println("t2 获取锁"); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2 end"); } finally { if (isLock) { lock.unlock(); } } }); t2.start(); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e3) { e3.printStackTrace(); } t2.interrupt();// 打断线程2的等待 }
公平锁
公平锁,在线程获取锁的时候,执行一定的公平策略。线程先申请锁,就会先获取锁,但是不能保证线程在执行的时候也是公平的。代码如下:
1 private static ReentrantLock lock = new ReentrantLock(true);// 参数为true的时候,表示为公平锁,等在前面的先执行 2 3 @Override 4 public void run() { 5 for (int i = 0; i < 100; i++) { 6 lock.lock(); 7 try { 8 System.out.println(Thread.currentThread().getName() + "执行了"); 9 } finally { 10 lock.unlock(); 11 } 12 } 13 } 14 15 public static void main(String[] args) { 16 T02_ReentrantLock5 rl = new T02_ReentrantLock5(); 17 Thread t1 = new Thread(rl); 18 Thread t2 = new Thread(rl); 19 t1.start(); 20 t2.start(); 21 22 }
分组唤醒
synchronized在线程wait时,如果唤醒,需要将所有当前锁下的线程全部唤醒,而ReentrantLock提供了一个分组唤醒的功能。通过 newCondition方法创建一个分组,然后通过分组.await()方法进行分组等待。通过分组.signalAll() 方法进行分组唤醒。现在用一个生产者消费者的案例进行说明:
1 { 2 final private LinkedList<T> lists = new LinkedList<>(); 3 final private int MAX = 10; 4 private volatile int count = 0; 5 private Lock lock = new ReentrantLock(); 6 // 生产者 7 private Condition producer = lock.newCondition(); 8 // 消费者 9 private Condition conSumer = lock.newCondition(); 10 11 public void put(T t){ 12 try { 13 lock.lock(); 14 while (count==MAX){ 15 System.out.println("队列已满,生产者等待"); 16 producer.await(); 17 System.out.println("生产者被唤醒"); 18 } 19 lists.add(t); 20 ++count; 21 System.out.println("加入对象,当前队列数量为"+count); 22 conSumer.signalAll(); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 }finally { 26 lock.unlock(); 27 } 28 29 } 30 public T get(){ 31 T t= null; 32 try { 33 lock.lock(); 34 while (count == 0) { 35 System.out.println("消费者等待"); 36 conSumer.await(); 37 System.out.println("消费者被唤醒"); 38 } 39 t= lists.getFirst(); 40 --count; 41 System.out.println("消费,当前队列数量为"+count); 42 System.out.println("消费者执行"); 43 producer.signalAll(); 44 45 } catch (InterruptedException e) { 46 e.printStackTrace(); 47 }finally { 48 lock.unlock(); 49 } 50 51 return t; 52 } 53 54 public static void main(String[] args) { 55 MyContainer1 qu= new MyContainer1(); 56 for (int i = 0; i < 10; i++) { 57 new Thread(()->{ 58 while (true){ 59 qu.put(new Object()); 60 } 61 }).start(); 62 } 63 for (int i = 0; i < 30; i++) { 64 new Thread(() -> { 65 while (true){ 66 qu.get(); 67 } 68 }).start(); 69 } 70 } 71 72 73 }