目录
- 公平锁
- 非公平锁
- 公平锁和非公平锁的用法
- 可重入锁
- synchronized可重入锁示例
- ReentrantLock的示例代码
- 死锁
- 死锁产生的原因
- 常用解决死锁的方法
- 判断程序是否发生死锁
- 死锁的案例(面试会问)
公平锁
多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远是队列的第一位获得锁。效率相对低 ,但是cpu 的利用高了
非公平锁
多个线程去获得锁的时候,会直接尝试获取,如果获取不到再去进入队列等待队列,如果获得锁就直接获得了。效率高,但是线程容易饿死(所有的工作,由一个线程完成)
公平锁和非公平锁的用法
在创建可重入锁时,想构造器中传入true
private final ReentrantLock lock = new ReentrantLock(true);
为什么可以这样子创建,大家一起看一下ReentrantLock的源码
// 在没有传入参数时,默认创建一个非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 当传入一个true值时,为公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁
指的是以线程为单位,当一个线程获得对象锁之后这个线程可以再次获取本对象锁,而其他线程是不可以的。synchronized和ReentrantLock都是可重入锁。
- sychronized是隐式锁,不用手工上锁与解锁,而lock为显示锁,需要手工上锁与解锁
- 可重入锁也叫递归锁
synchronized可重入锁示例
/**
* 演示可重入锁是什么意思,可重入,就是可以重复获取相同的锁而不会出现死锁
* synchronized和ReentrantLock都是可重入的
* */
public class WhatReentrantSynchronized {
// 创建一个锁对象
static Object mylock = new Object();
public static void main(String[] args) {
new Thread(()->{
// 创建第一个锁
synchronized (mylock){
System.out.println("这是第一层锁");
synchronized (mylock){
System.out.println("这是第二层锁");
}
}
}).start();
}
}
ReentrantLock的示例代码
/**
* lock和unlock的数量必须一致,否则会出现死锁
* */
public class WhatReentrantLock {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(()->{
// 上锁
lock.lock();
try {
System.out.println("这是第一层锁");
// 再次上锁
lock.lock();
try{
System.out.println("这是第二层锁");
}finally {
lock.unlock();
}
}finally {
lock.unlock();
}
}).start();
}
}
死锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。
当多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进,这种情况就是死锁。
很显然,如果没有外力的作用,那么死锁涉及到的各个进程都将永远处于封锁状态。
死锁产生的原因
- 系统资源不足
- 系统资源分配不当
- 进程运行顺序不当
常用解决死锁的方法
-
如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
-
在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率。
-
对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。
-
如果业务处理不好,可以用分布式事务锁或者使用乐观锁。
判断程序是否发生死锁
- jps 类似于linux中的 ps -ef查看进程号
- jstack 自带的堆栈跟踪工具
死锁的案例(面试会问)
public class DeadLock {
//创建两个对象
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(()->{
// 获取a这把锁
synchronized (a) {
System.out.println(Thread.currentThread().getName()+" 持有锁a,试图获取锁b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b) {
System.out.println(Thread.currentThread().getName()+" 获取锁b");
}
}
},"A").start();
new Thread(()->{
// 获取b这把锁
synchronized (b) {
System.out.println(Thread.currentThread().getName()+" 持有锁b,试图获取锁a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a) {
System.out.println(Thread.currentThread().getName()+" 获取锁a");
}
}
},"B").start();
}
}