锁
701字约2分钟
2024-08-08
公平锁、非公平锁
公平锁:非常公平,不能插队,必须先来后到
非公平锁:非常不公平,可以插队(默认都是非公平锁,考虑效率问题)
可重入锁
可重入锁(递归锁)
Synchronized 版
两个方法都是一把锁
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.send();
}, "A").start();
new Thread(() -> {
phone.send();
}, "B").start();
}
class Phone {
public synchronized void send() {
System.out.println(Thread.currentThread().getName() + "-> send message");
call();
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> call");
}
}
// A-> send message
// A -> call
// B-> send message
// B -> call
Lock
版
两把锁,lock
锁必须配对,否则会死锁。
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.send();
}, "A").start();
new Thread(() -> {
phone.send();
}, "B").start();
}
class Phone {
Lock lock = new ReentrantLock();
public void send() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "-> send message");
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " -> call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
// A-> send message
// A -> call
// B-> send message
// B -> call
自旋锁
public static void main(String[] args) throws InterruptedException {
SpinlockDemo lock = new SpinlockDemo();
new Thread(() -> {
lock.mylock();
try {
// 延迟 4 秒后解锁
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.myUnlock();
}
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
lock.mylock();
try {
// 延迟 1 秒后解锁
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.myUnlock();
}
}, "B").start();
}
// A => 尝试加锁
// A => 加锁成功
// B => 尝试加锁
// A => 解锁
// B => 加锁成功
// B => 解锁
public class SpinlockDemo {
// Thread 初始为 null
AtomicReference<Thread> atomicReference = new AtomicReference<>();
// 加锁
public void mylock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + " => 尝试加锁");
// 自旋锁
while (!atomicReference.compareAndSet(null, thread)) {
}
System.out.println(Thread.currentThread().getName() + " => 加锁成功");
}
// 解锁
public void myUnlock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + " => 解锁");
atomicReference.compareAndSet(thread, null);
}
}
死锁
死锁的四个必要条件:
互斥条件:进程对所分配到的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待
请求和保持条件:进程已经获得了至少一个资源,但又对其他资源发出请求,而该资源已被其他进程占有,此时该进程的请求被阻塞,但又对自己获得的资源保持不放
不可剥夺条件:进程已获得的资源在未使用完毕之前,不可被其他进程强行剥夺,只能由自己释放
环路等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合
{Pl, P2, …, pn}
,其中Pi
等待的资源被P(i+1)
占有(i=0, 1, …, n-1)
,Pn
等待的资源被P0
占有
死锁排查
1、使用 jsp -l
定位进程号
2、使用 jstack 进程号
查看进行信息
这里面就能看到是否有死锁(Found 1 deadlock
)