多线程中的各种锁 1. 公平锁、非公平锁 1.1 概念:
公平锁就是先来后到、非公平锁就是允许加塞 Lock lock = new ReentrantLock(Boolean fair); 默认非公平
公平锁是指多个线程按照申请锁的顺序来获取锁,类似排队打饭。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者节现象。
1.2 两者区别?
公平锁:
Threads acquire a fair lock in the order in which they requested it
公平锁,就是很公平,在并发环境中,每个线程在获取锁时,会先查看此锁维护的等待队列,如果为空,或者当前线程就是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己
非公平锁:
a nonfair lock permits barging: threads requesting a lock can jump ahead of the queue of waiting threads if the lock happens to be available when it is requested
非公平锁比较粗鲁,上来就直接尝试占有额,如果尝试失败,就再采用类似公平锁那种方式。
1.3 如何体现公平非公平?对Java ReentrantLock而言,通过构造函数指定该锁是否公平,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大
对Synchronized而言,是一种非公平锁
其中ReentrantLock和Synchronized默认都是非公平锁,默认都是可重入锁
2. 可重入锁(递归锁)前文提到ReentrantLock和Synchronized默认都是非公平锁,默认都是可重入锁,那么什么是可重入锁?
2.1 概念指的时同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块
2.2 为什么要用到可重入锁?可重入锁最大的作用是避免死锁
ReentrantLock/Synchronized 就是一个典型的可重入锁
2.3 代码验证可重入锁?首先我们先验证ReentrantLock
package com.yuxue.juc.lockDemo; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 尝试验证ReentrantLock锁的可重入性的Demo * */ public class ReentrantLockDemo { public static void main(String[] args) { mobile mobile = new mobile(); new Thread(mobile,"t1").start(); new Thread(mobile,"t2").start(); } } /** * 辅助类mobile,首先继承了Runnable接口,可以重写run方法 * 内部主要有两个方法 * run方法首先调用第一个方法 * */ class mobile implements Runnable { Lock lock = new ReentrantLock(); //run方法首先调用第一个方法 @Override public void run() { testMethod01(); } //第一个方法,目的是首先让线程进入方法1 public void testMethod01() { //加锁 lock.lock(); try { //验证线程进入方法1 System.out.println(Thread.currentThread().getName() + "\t" + "get in the method1"); //休眠 Thread.sleep(2000); //进入方法2 testMethod02(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } //第二个方法。目的是验证ReentrantLock是否是可重入锁 public void testMethod02() { lock.lock(); try { System.out.println("==========" + Thread.currentThread().getName() + "\t" + "leave the method1 get in the method2"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }因为同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块
之后观察输出结果为:
t1 get in the method1 ==========t1 leave the method1 get in the method2 t2 get in the method1 ==========t2 leave the method1 get in the method2意味着线程t1进入方法1之后,再进入方法2,也就是说进入内层方法自动获取锁,之后释放方法2的那把锁,再释放方法1的那把锁,这之后线程t2才能获取到方法1的锁,才可以进入方法1
同样地,如果我们在方法1中再加一把锁,不给其解锁,也就是
那么结果会是怎么呢?我们运行代码可以得到
我们发现线程是停不下来的,线程t1进入方法1加了两把锁,之后进入t2,但是退出t1的方法过程中没有解锁,这就导致了t2线程无法拿到锁,也就验证了锁重入的问题
那么为了验证是同一把锁,我们在方法1对其加锁两次,方法2对其解锁两次可以吗?这锁是相同的吗?也就意味着:
我们再次运行,发现结果为:
t1 get in the method1 ==========t1 leave the method1 get in the method2 t2 get in the method1 ==========t2 leave the method1 get in the method2也就侧面验证了加锁的是同一把锁,更验证了我们的锁重入问题