1. 公平锁与非公平锁
1)公平锁
当多个线程按照申请锁的顺序排队获取锁,获取不到锁的进入阻塞队列,按照顺序依次获取锁
Lock lock= new ReentrantLock(true);
2)非公平锁
多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程先获得锁,在高并发的场景下,有可能造成优先级反转或者某个线程一直得不到锁
synchronized 是非公平锁,Synchronized 是通过对象内部的叫一个监视器锁(monitor)来实现的。这种依赖于操作系统的 mutex lock 所实现的锁称之为重量级锁
ReentrantLock 可以进行选择设置,默认是非公平锁,ReentrantLock 是基于AQS的
其原理大致为:当某一线程获取锁后,将state值+1,并记录下当前持有锁的线程,再有线程来获取锁时,判断这个线程与持有锁的线程是否是同一个线程,如果是,将state值再+1,如果不是,阻塞线程。 当线程释放锁时,将state值-1,当state值减为0时,表示当前线程彻底释放了锁,然后将记录当前持有锁的线程的那个字段设置为null,并唤醒其他线程,使其重新竞争锁
Lock lock= new ReentrantLock(false);
// 默认是 false
Lock lock= new ReentrantLock();
2. 可重入锁
可重入锁:当已获取锁的线程,在访问该锁资源的其余代码时,无需再获取锁,可直接进入,称之为可重入锁;可重入锁能有效避免死锁的情况
public class Phone {
// 类锁
public static synchronized void sendEmail(){
sendSMS(Thread.currentThread().getName());
System.out.println("-----sendEmail");
}
// 类锁
public static synchronized void sendSMS(String threadName){
System.out.println(threadName + "-----sendSMS");
}
public void sendHello(){
System.out.println("-----sendHello");
}
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendEmail();
},"t1").start();
new Thread(()->{
phone.sendSMS(Thread.currentThread().getName());
},"t2").start();
new Thread(()->{
phone.sendHello();
},"t3").start();
}
}
执行结果:
当 t1 线程获得类锁之后,去调用 sendSMS 需要获得该方法的类锁,但是之前在执行 sendEmail 时候已经获得该类锁,所以此时无需再次获得,直接执行,这个就叫可重入锁;synchronized 和 ReentrantLock 都是可重入锁,当 t1 执行完毕后,释放了类锁,t2 线程才能进入其执行方法 sendSMS
ReentrantLock 和 synchronized 都是悲观锁,非公平锁,可重入锁
3. Lock 接口及其实现类 ReentrantLock
Java 中默认对资源上锁使用 synchronized 关键字,但是 synchronized 关键字功能有限,Java 1.5以后又在 JUC 包下设计了一个新的一套对资源上锁和解锁的代码
Lock 接口以及其实现类
使用方式:
// 获取ReentrantLock对象
private ReentrantLock lock = new ReentrantLock();
// 加锁 获取不到锁会阻塞
lock.lock();
try {
System.out.println("this a thread lock");
}finally {
// 释放锁 如果不释放其他线程就获取不到锁
lock.unlock();
}
synchronized 和 ReentrantLock区别:
- synchronized 是Java的关键字,基于jvm实现,ReentrantLock 是juc包下面的类库,是api层面的锁
- synchronized 会自动加锁和释放锁,ReentrantLock 需要手动调用 lock() 方法和 unlock() 方法加锁和释放锁
-
synchronized 可以用在普通方法上、使用在静态方法上、使用在代码块中,ReentrantLock 只能在代码块中使用
-
ReentrantLock 支持可响应中断,synchronized 不支持响应中断
-
synchronized 只能支持非公平锁,ReentrantLock 支持公平锁和非公平锁
-
ReentrantLock 配合 condition 可以绑定多个条件,实现多个阻塞队列,能够更加精准的加锁和释放锁