原创

Java锁

温馨提示:
本文最后更新于 2025年07月19日,已超过 329 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

乐观锁和悲观锁

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
  1. 选择进程,点击 连接
  2. 点击 不安全的连接
  3. 选择 线程 栏,
  4. 点击 左下角 检测死锁
  5. 此时 自动展示 死锁栏  A 和 B

亦是 死锁的 证明方式之一

正文到此结束