公平锁、非公平锁
一、理解概念
公平锁:非常公平,不允许插队,必须先来后到
非公平锁(系统默认):允许插队,避免一个3秒的线程,等待一个3小时的线程执行完毕才执行
二、设置公平锁的方式
//创建锁的时候,加上一个true就会设置为公平锁
ReentrantLock reentrantLock = new ReentrantLock(true);
乐观锁和悲观锁
可重入锁
一、理解
所有的锁,都叫可重入锁,也叫递归锁。拿到了外面的锁,自动就获得了里面的锁
二、测试
//测试可重入锁
public class Test01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName() + "执行了发短信!");
call();
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName() + "执行了打电话!");
}
}
控制台:
线程A执行完sms方法,并不会释放锁,反而会进入到call方法中,当call方法执行完毕才真正释放锁,然后B线程才会执行
自旋锁
一、理解自旋锁
自旋锁的底层原理就是一个while循环,如果条件不满足,就会一直处在while循环中出不来,其他的线程也就拿不到对应的锁,不能执行对应的任务,因此就保证了线程的安全
二、手写自旋锁
/*自旋锁
* */
public class SpinLock {
//创建一个原子引用类对象,这样就可以调用CAS方法
AtomicReference<Thread> atomicReference = new AtomicReference<Thread>();
//加锁的方法
public void myLock(){
//得到当前的线程
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "-->myLock");
//自旋锁的关键,如果第一个线程没有释放锁,那么这个while循环一直都是true死循环,不能往下执行业务代码
while (!atomicReference.compareAndSet(null,thread)){
}
}
//解锁的方法
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "-->myUnLock");
//解锁不需要循环,如果真正走到解锁的步骤,直接痛快的把原子引用类中的thread更新成null,那么其他的线程才可以拿到锁,进行加锁的操作
atomicReference.compareAndSet(thread,null);
}
}
三、测试
public class TestSpinLock {
public static void main(String[] args) throws InterruptedException {
//创建自定义锁的对象,等价于之前定义的ReentrantLock可重入锁对象
SpinLock lock = new SpinLock();
//创建线程A
new Thread(()->{
lock.myLock(); //获得锁
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.myUnLock(); //解锁
}
},"A").start();
//模拟延时
TimeUnit.SECONDS.sleep(3);
//创建线程B
new Thread(()->{
lock.myLock(); //获得锁
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.myUnLock(); //解锁
}
},"B").start();
}
}
控制台:
只有当A线程拿到锁,执行完业务代码,释放锁,B线程才能获得锁,执行他的业务代码,因为线程B可能会在线程A执行的时候想要拿到锁,但是A没释放锁,线程B一直会在得到锁的myLock方法中的while中死循环,即持续自旋
死锁理解和排查死锁
一、理解死锁
互相想读取对方的锁
二、排查死锁现象的方法
1、使用jps -l命令,在idea的Terminal中查看当前存活的进程,定位进程号
2、输入命令jstack + 进程号,查看问题
往下翻提示信息,查看线程的堆栈信息,就能找到死锁问题