Java锁
乐观锁和悲观锁
synchronized和Lovk的实现类都是悲观锁
悲观锁: 认为自己在使用数据的时候一定会有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改
适合写操作多的场景,先加锁可以保证写操作是数据正确
显示的锁定之后再操作同步资源。
乐观锁:认为自己在使用数据时不会有别的线程来修改数据或资源,所以不会添加锁
Java中通过无锁编程来实现。
如果这个数据没有被更新,当前线程将自己修改的数据成功写入。
如果这个数据被其他线程更新,则根据不同的 实现方式执行不同的操作,比如放弃修改、重试强锁等
判断规则:
- 版本号机制 version
- 最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
synchronized:
作用于 实例方法,当前实例加锁,进入同步代码块前获得当前实例的锁
作用于 代码块,括号里的配置的对象加锁。 实现使用的是 monitorenter和monitorexit指令(普通->1:2;抛出异常时->1:1)
作用于 静态方法:当前类加锁,进去同步代码之前要获得当前类对象的锁。
普通同步方法(对象锁):反编译时,会有标识:ACC_SYNCHRONIZED 。调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。如果设置了,执行线程将会先持有monitor锁,然后再执行方法,最后在方法我那成(无论是正常还是异常完成)时释放monitor。
静态同步方法(类锁):反编译时,含有 ACC_SYNCHRONIZED 和ACC_STATIC标识。
锁<=>管程<=>监视器<=>Monitors :是一种程序结构,结构内的多个子程序(对象或模块),形成的多个工作线程互斥访问共享资源。
任何一个对象都可以成为一个锁?
每个对象天生带着一个对象监视器
每一个被锁住的对象都会和Monitor关联起来
公平锁和非公平锁
非公平锁:抢夺锁、获得锁的几率不公平。高并发下,可能造成优先级反转或者饥饿的状态(某个线程一直得不到锁)。=>后来的可能先获得锁
ReentrantLock 不加参数 是非公平锁,ReentrantLock(true) 是公平锁
公平锁:多个线程按照申请锁的顺序来获取锁。=>先来先得
非公平锁的效率 > 公平锁的效率
使用场景:
非公平锁具有更高的吞吐量,节省了很多线程切换的时间。
可重入锁(递归锁)
指在同一个线程的外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提:锁对象时同一个对象),不会因为之前还没释放锁而阻塞。
Java中ReentrantLock和synchronized都是可重入锁。
优点:可一定程度避免死锁
进入 同步域(同步代码块/方法或显示锁定的代码)
可重入锁种类
隐式:synchronized ,
在一个synchronized修饰的方法或代码块的灭不调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的。
显式:Lock-.>ReentrantLock :显式调用,lock和unlock需要一一匹配。加锁几次就要解锁几次。加解锁次数不一致的情况,可能会导致线程阻塞或死锁。
死锁及排查
示例
final Object objectA = new Object();
final Object objectB = new Object();
new Thread(() -> {
synchronized (objectA) {
System.out.println("线程A 获取了objectA锁,希望获取objectB 锁");
try {
Thread.sleep(1000);
synchronized (objectB) {
System.out.println("线程A 获取了objectB锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
synchronized (objectB) {
System.out.println("线程A 获取了objectA锁,希望获取objectA 锁");
try {
Thread.sleep(1000);
synchronized (objectA) {
System.out.println("线程A 获取了objectB锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start(); 产生死锁的原因
- 系统资源不足
- 进程运行推进的顺序不合适
- 资源分配不当
排查死锁
查看进程 终端输入
jps -l 结果
PS E:\knife_demo> jps -l
296436
36432 sun.tools.jps.Jps
102852 org.jetbrains.idea.maven.server.RemoteMavenServer36
230712 com.xyb.demo.controller.Knife4jTestController
3804 org.jetbrains.jps.cmdline.Launcher 根据进程号查看 Knife4jTestController 类
jstack 230712 结果
PS E:\knife_demo> jstack 230712
2025-06-02 03:04:22
Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.1+12-LTS-39 mixed mode, sharing):
Threads class SMR info:
………省略部分………
"VM Thread" os_prio=2 cpu=0.00ms elapsed=355.38s tid=0x000001c10b4d5930 nid=0x570fc runnable
"GC Thread#0" os_prio=2 cpu=0.00ms elapsed=355.39s tid=0x000001c1662dd490 nid=0x4a364 runnable
at com.xyb.demo.controller.Knife4jTestController$$Lambda$16/0x0000000800c01470.run(Unknown Source)
at java.lang.Thread.run(java.base@17.0.1/Thread.java:833)
Found 1 deadlock.
Found 1 deadLock ===>发现一个死锁
Java图形化 jconsole
终端输入
jconsole - 选择进程,点击 连接
- 点击 不安全的连接
- 选择 线程 栏,
- 点击 左下角 检测死锁
- 此时 自动展示 死锁栏 A 和 B
亦是 死锁的 证明方式之一
- 本文标签: Java
- 本文链接: http://119.91.109.247:8443//article/79
- 版权声明: 本文由张亚东原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权