Java JUC并发之对于各种锁的理解以及如何解决死锁问题
对于各种锁的理解
-
公平锁、非公平锁
公平锁 : 不允许插队,必须先来后到
非公平锁: 允许插队(默认都是非公平锁)
-
可重入锁
synchronized 这里是同一把锁
package com.liu.lock8.reentrantLock; public class Demo01 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ phone.sendMsg(); },"A").start(); new Thread(()->{ phone.sendMsg(); },"B").start(); } } class Phone{ public synchronized void sendMsg() { System.out.println(Thread.currentThread().getName() + " => sendMsg()"); call(); // 这里也有锁 } public synchronized void call() { System.out.println(Thread.currentThread().getName() + " => call()"); } }
lock 这里是两把不同的锁
package com.liu.lock8.reentrantLock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Demo02 { public static void main(String[] args) { Phone02 phone = new Phone02(); new Thread(()->{ phone.sendMsg(); },"A").start(); new Thread(()->{ phone.sendMsg(); },"B").start(); } } class Phone02{ Lock lock = new ReentrantLock(); public void sendMsg() { lock.lock(); // 一共有两把锁 // lock锁必须配对,否则会死锁 try { System.out.println(Thread.currentThread().getName() + " => sendMsg()"); call(); // 这里也有锁 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void call() { lock.lock(); lock.lock(); try { System.out.println(Thread.currentThread().getName() + " => call()"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
可重入锁(递归锁)=> 可以递归调用和嵌套
- 拿到外面的多之后,就可以自动获得里面的锁
- lock锁必须配对,否则会导致死锁
-
自旋锁 spinlock
自己写一个自旋锁
package com.liu.lock8.spinlock;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁
*/
public class SpinlockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
// 加锁
public void myLock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + " ==> mylock");
// 自旋锁
while (!atomicReference.compareAndSet(null, thread)) {
System.out.println(Thread.currentThread().getName() + "自旋中");
}
}
// 解锁
public void myUnLock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + " ==> myUnlock");
atomicReference.compareAndSet(thread, null);
}
}
运行结果: 线程T1先获得自旋锁,T1进入休眠状态,之后线程T2获得自旋锁,而T2一直在自旋,直到T1休眠结束,释放锁,线程T2才停止自旋并解锁。
-
死锁
什么是死锁?
模拟死锁:
package com.liu.lock8.deadlock;
import java.util.concurrent.TimeUnit;
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable {
private String lockA; // 这里的锁指的是要锁定的资源类对象
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + " lock: " + lockA +",try to get "+lockB);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "lock: " + lockB + ",try to get " + lockA);
}
}
}
}
运行结果: T1获取到了lockA,但是同时想要拿到lockB;
T2获取到了lockB,但是同时想要拿到lockA;
两个线程一直在等待对方释放锁,线程无法停止下来,于是便会导致死锁!
解决死锁办法 在IDEA终端上可以使用这些命令排查问题
- 使用 jps -l查询运行的进程号
- 使用 jstack 进程号 => 查看死锁信息
面试: 工作中如何排查问题(死锁)
- 日志
- 堆栈 jstack
本文来自博客园,作者:{夕立君},转载请注明原文链接:https://www.cnblogs.com/xili-sanriyue/p/15028549.html