【JUC】Java锁介绍

文章目录

  • 阿里锁开发规范
  • 乐观锁和悲观锁
    • 悲观锁
    • 乐观锁
  • synchronized 类锁、对象锁
    • synchronized有三种应用方式
    • 锁相关的8种案例演示(对象锁、类锁)
      • 标准访问ab两个线程,请问先打印邮件还是短信?
      • sendEmail钟加入暂停3秒钟,请问先打印邮件还是短信?
      • 添加一个普通的hello方法,然后在b线程中调用,请问先打印普通方法还是邮件?
      • 有两部手机,请问先打印邮件还是短信?
      • 有两个静态同步方法,一步手机, 请问先打印邮件还是短信?
      • 有两个静态同步方法,两部手机, 请问先打印邮件还是短信?
      • 有一个静态同步方法 一个普通同步方法,请问先打印邮件还是短信?
      • 有一个静态同步方法,一个普通同步方法,两部手机,请问先打印邮件还是短信?
      • **结论**
      • notify方法说明
    • 从字节码角度分析synchronized实现
      • synchronized同步代码块
      • synchronized普通同步方法
      • synchronized静态同步方法
    • 反编译synchronized锁的是什么
    • 为什么任何一个对象都可以成为一个锁?
      • 源码分析
  • 公平锁和非公平锁
    • 何为公平锁/非公平锁
      • 面试题
    • 预埋伏AQS
  • 可重入锁(递归锁)
    • 概念说明
    • 可重入锁种类
      • 隐式锁(即synchronized关键字使用的锁)
        • synchronized可重入原理分析
      • 显式锁(如ReentrantLock)
  • 死锁及排查
    • 概念
    • 产生原因
    • 死锁案例
    • 如何排查死锁
      • 纯命令
      • 图形化
  • 写锁(独占锁)/读锁(共享锁)
  • 自旋锁spinLock
  • 无锁->独占锁->读写锁->邮戳锁
  • 无锁->偏向锁->轻量锁->重量锁
  • 文章说明

阿里锁开发规范

【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。说明:使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC方法(RPC涉及网络通讯,延迟较高)。

乐观锁和悲观锁

悲观锁

  • 认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改
  • synchronized和Lock的实现类都是悲观锁,适合写操作多的场景,先加锁可以保证写操作时数据正确
  • 显示的锁定之后再操作同步资源-----狼性锁

【synchronized】

// 悲观锁的调用方式
public synchronized void m1(){// 加锁后的业务逻辑......
}

【Lock】

// 保证多个线程使用的是同一个Lock对象的前提下
ReentrantLock lock = new ReentrantLock();
public void m2() {lock.lock();try {// 操作同步资源} finally {lock.unlock();}
}

乐观锁

  • 认为自己在使用数据的时候不会有别的线程修改数据或资源,不会添加锁
  • Java中使用无锁编程来实现,只是在更新的时候去判断,之前有没有别的线程更新了这个数据
    • 如果这个数据没有被更新,当前线程将自己修改的数据成功写入
    • 如果已经被其他线程更新,则根据不同的实现方式执行不同的操作,比如:放弃修改、重试抢锁等等。判断规则有:
      • 版本号机制Version(获取数据的时候版本是1,修改之后,版本号是2;如果修改的时候发现版本号不再是1,说明已经有其他线程修改了,)
      • 最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
  • 适合读操作多的场景,不加锁的特性能够使其读操作的性能大幅提升,乐观锁则直接去操作同步资源,是一种无锁算法
// 悲观锁的调用方式
private AtomicInteger atomicInteger = new AtomicInteger();
atomicInteger.incrementAndGet();

synchronized 类锁、对象锁

synchronized有三种应用方式

  • 作用于实例方法,当前实例加锁,进入同步代码块前要获得当前实例的锁
  • 作用于代码块,对括号里配置的对象加锁
  • 作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁

锁相关的8种案例演示(对象锁、类锁)

标准访问ab两个线程,请问先打印邮件还是短信?

/**
* 资源类
**/
class Phone {public synchronized void sendEmail() {System.out.println("------sendEmail");}public synchronized void sendSMS() {System.out.println("------sendSMS");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendSMS();}, "b").start();}
}
  • 答:先邮件,后短信 ,因为共用一个对象锁(sendEmail执行完成才能执行sendSMS)
  • 例如两个人去用一台手机,等第一个人用手机发完邮件,第二个人才可以发短信
  • 一个对象里面如果有多个synchronized方法,某一个时刻内,只有一个线程能去调用其中的一个synchronized方法了,其它的线程都只能等待。换句话然某一个时刻内,只能有唯一的一个线程去访问其中一个synchronized方法。锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它synchronized方法

sendEmail钟加入暂停3秒钟,请问先打印邮件还是短信?

/**
* 资源类
**/
class Phone {public synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------sendEmail");}public synchronized void sendSMS() {System.out.println("------sendSMS");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendSMS();}, "b").start();}
}
  • 答:先邮件,后短信 共用一个对象锁

添加一个普通的hello方法,然后在b线程中调用,请问先打印普通方法还是邮件?

/**
* 资源类
**/
class Phone {public synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------sendEmail");}public synchronized void sendSMS() {System.out.println("------sendSMS");}public void hello() {System.out.println("------hello");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.hello();}, "b").start();}
}
  • 答:先hello,再邮件
  • 你用手机来发邮件,我借你手机充电线来充电,不发生资源的争抢

有两部手机,请问先打印邮件还是短信?

/**
* 资源类
**/
class Phone {public synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------sendEmail");}public synchronized void sendSMS() {System.out.println("------sendSMS");}public void hello() {System.out.println("------hello");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone1 = new Phone();Phone phone2 = new Phone();new Thread(() -> {phone1.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone2.sendSMS();}, "b").start();}
}
  • 答:先短信后邮件,资源没有争抢,不是同一个对象锁

有两个静态同步方法,一步手机, 请问先打印邮件还是短信?

/**
* 资源类
**/
class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------sendEmail");}public static synchronized void sendSMS() {System.out.println("------sendSMS");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendSMS();}, "b").start();}
}
  • 答:先邮件后短信 ,共用一个类锁

有两个静态同步方法,两部手机, 请问先打印邮件还是短信?

/**
* 资源类
**/
class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------sendEmail");}public static synchronized void sendSMS() {System.out.println("------sendSMS");}public void hello() {System.out.println("------hello");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone1 = new Phone();Phone phone2 = new Phone();new Thread(() -> {phone1.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone2.sendSMS();}, "b").start();}
}
  • 答:先邮件后短信,共用一个类锁

有一个静态同步方法 一个普通同步方法,请问先打印邮件还是短信?

/**
* 资源类
**/
class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------sendEmail");}public synchronized void sendSMS() {System.out.println("------sendSMS");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendSMS();}, "b").start();}
}
  • 答:先短信后邮件(邮件有延迟) ,一个用类锁一个用对象锁,两个锁不一样,不产生竞争

有一个静态同步方法,一个普通同步方法,两部手机,请问先打印邮件还是短信?

/**
* 资源类
**/
class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------sendEmail");}public synchronized void sendSMS() {System.out.println("------sendSMS");}public void hello() {System.out.println("------hello");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone1 = new Phone();Phone phone2 = new Phone();new Thread(() -> {phone1.sendEmail();}, "a").start();try {// 保存a线程启动之后再来启动b线程TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone2.sendSMS();}, "b").start();}
}
  • 答:先短信后邮件,一个类锁一个对象锁

结论

  • 当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁
  • 对于普通同步方法,锁的是当前实例对象,通常指this(就是new出来的具体实例对象本身),一个对象的所有普通同步方法用的都是同一把锁(对象锁)。也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁
  • 对于静态同步方法,锁的是当前类的Class对象(唯一模板Class),如Phone.class(类锁)
  • 具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞争的
  • 对于同步方法块,锁的是synchronized括号内的对象,传入什么锁什么
  • 一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁

在这里插入图片描述

在这里插入图片描述

类模板存储在方法区,new出来的对象在堆里面

notify方法说明

线程想持有一个锁的话,需要满足如下三个条件之一:

  • 执行一个synchronized实例方法
  • 执行同步代码块
  • 执行静态同步方法

从字节码角度分析synchronized实现

  • javap -c ***.class 文件反编译
  • 需要更多信息:javap -v *.class文件反编译
    • -v -verbose:输出附加信息(包括行号、本地变量表,反汇编等详细信息)
public class LockSyncDemo {Object object = new Object();public void m1() {synchronized (object) {System.out.println("----hello synchronized code block");throw new RuntimeException("-----exp");}}public synchronized void m2() {System.out.println("----hello synchronized m2");}public static synchronized void m3() {System.out.println("----hello static synchronized m3");}public static void main(String[] args) {}
}

在这里插入图片描述

javap -c
PS D:\Projects\juc_bilibili\target\classes\com\bilibili\juc\locks> javap -c .\LockSyncDemo.class
Compiled from "LockSyncDemo.java"
public class com.bilibili.juc.locks.LockSyncDemo {java.lang.Object object;public com.bilibili.juc.locks.LockSyncDemo();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: aload_05: new           #2                  // class java/lang/Object8: dup9: invokespecial #1                  // Method java/lang/Object."<init>":()V12: putfield      #3                  // Field object:Ljava/lang/Object;15: returnpublic void m1();Code:0: aload_01: getfield      #3                  // Field object:Ljava/lang/Object;4: dup5: astore_16: monitorenter7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;10: ldc           #5                  // String ----hello synchronized code block12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V15: new           #7                  // class java/lang/RuntimeException18: dup19: ldc           #8                  // String -----exp21: invokespecial #9                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V24: athrow25: astore_226: aload_127: monitorexit28: aload_229: athrowException table:from    to  target type7    28    25   anypublic synchronized void m2();Code:0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #10                 // String ----hello synchronized m25: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnpublic static synchronized void m3();Code:0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #11                 // String ----hello static synchronized m35: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnpublic static void main(java.lang.String[]);Code:0: return
}
javap -v
PS D:\Projects\juc_bilibili\target\classes\com\bilibili\juc\locks> javap -v .\LockSyncDemo.class
Classfile /D:/Projects/juc_bilibili/target/classes/com/bilibili/juc/locks/LockSyncDemo.classLast modified 2024年7月16日; size 1125 bytesSHA-256 checksum 705394b1f660417439abfb88585f1fc108b5caf625d01bc4fbfa19a8f9aad705Compiled from "LockSyncDemo.java"
public class com.bilibili.juc.locks.LockSyncDemominor version: 0major version: 52flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #12                         // com/bilibili/juc/locks/LockSyncDemosuper_class: #2                         // java/lang/Objectinterfaces: 0, fields: 1, methods: 5, attributes: 1
Constant pool:#1 = Methodref          #2.#36         // java/lang/Object."<init>":()V#2 = Class              #37            // java/lang/Object#3 = Fieldref           #12.#38        // com/bilibili/juc/locks/LockSyncDemo.object:Ljava/lang/Object;#4 = Fieldref           #39.#40        // java/lang/System.out:Ljava/io/PrintStream;#5 = String             #41            // ----hello synchronized code block#6 = Methodref          #42.#43        // java/io/PrintStream.println:(Ljava/lang/String;)V#7 = Class              #44            // java/lang/RuntimeException#8 = String             #45            // -----exp#9 = Methodref          #7.#46         // java/lang/RuntimeException."<init>":(Ljava/lang/String;)V#10 = String             #47            // ----hello synchronized m2#11 = String             #48            // ----hello static synchronized m3#12 = Class              #49            // com/bilibili/juc/locks/LockSyncDemo#13 = Utf8               object#14 = Utf8               Ljava/lang/Object;#15 = Utf8               <init>#16 = Utf8               ()V#17 = Utf8               Code#18 = Utf8               LineNumberTable#19 = Utf8               LocalVariableTable#20 = Utf8               this#21 = Utf8               Lcom/bilibili/juc/locks/LockSyncDemo;#22 = Utf8               m1#23 = Utf8               StackMapTable#24 = Class              #49            // com/bilibili/juc/locks/LockSyncDemo#25 = Class              #37            // java/lang/Object#26 = Class              #50            // java/lang/Throwable#27 = Utf8               m2#28 = Utf8               m3#29 = Utf8               main#30 = Utf8               ([Ljava/lang/String;)V#31 = Utf8               args#32 = Utf8               [Ljava/lang/String;#33 = Utf8               MethodParameters#34 = Utf8               SourceFile#35 = Utf8               LockSyncDemo.java#36 = NameAndType        #15:#16        // "<init>":()V#37 = Utf8               java/lang/Object#38 = NameAndType        #13:#14        // object:Ljava/lang/Object;#39 = Class              #51            // java/lang/System#40 = NameAndType        #52:#53        // out:Ljava/io/PrintStream;#41 = Utf8               ----hello synchronized code block#42 = Class              #54            // java/io/PrintStream#43 = NameAndType        #55:#56        // println:(Ljava/lang/String;)V#44 = Utf8               java/lang/RuntimeException#45 = Utf8               -----exp#46 = NameAndType        #15:#56        // "<init>":(Ljava/lang/String;)V#47 = Utf8               ----hello synchronized m2#48 = Utf8               ----hello static synchronized m3#49 = Utf8               com/bilibili/juc/locks/LockSyncDemo#50 = Utf8               java/lang/Throwable#51 = Utf8               java/lang/System#52 = Utf8               out#53 = Utf8               Ljava/io/PrintStream;#54 = Utf8               java/io/PrintStream#55 = Utf8               println#56 = Utf8               (Ljava/lang/String;)V
{java.lang.Object object;descriptor: Ljava/lang/Object;flags: (0x0000)public com.bilibili.juc.locks.LockSyncDemo();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=3, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: aload_05: new           #2                  // class java/lang/Object8: dup9: invokespecial #1                  // Method java/lang/Object."<init>":()V12: putfield      #3                  // Field object:Ljava/lang/Object;15: returnLineNumberTable:line 10: 0line 11: 4LocalVariableTable:Start  Length  Slot  Name   Signature0      16     0  this   Lcom/bilibili/juc/locks/LockSyncDemo;public void m1();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=3, locals=3, args_size=10: aload_01: getfield      #3                  // Field object:Ljava/lang/Object;4: dup5: astore_16: monitorenter7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;10: ldc           #5                  // String ----hello synchronized code block12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V15: new           #7                  // class java/lang/RuntimeException18: dup19: ldc           #8                  // String -----exp21: invokespecial #9                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V24: athrow25: astore_226: aload_127: monitorexit28: aload_229: athrowException table:from    to  target type7    28    25   anyLineNumberTable:line 14: 0line 15: 7line 16: 15line 17: 25LocalVariableTable:Start  Length  Slot  Name   Signature0      30     0  this   Lcom/bilibili/juc/locks/LockSyncDemo;StackMapTable: number_of_entries = 1frame_type = 255 /* full_frame */offset_delta = 25locals = [ class com/bilibili/juc/locks/LockSyncDemo, class java/lang/Object ]stack = [ class java/lang/Throwable ]public synchronized void m2();descriptor: ()Vflags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZEDCode:stack=2, locals=1, args_size=10: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #10                 // String ----hello synchronized m25: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 21: 0line 22: 8LocalVariableTable:Start  Length  Slot  Name   Signature0       9     0  this   Lcom/bilibili/juc/locks/LockSyncDemo;public static synchronized void m3();descriptor: ()Vflags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZEDCode:stack=2, locals=0, args_size=00: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #11                 // String ----hello static synchronized m35: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 25: 0line 26: 8public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=0, locals=1, args_size=10: returnLineNumberTable:line 31: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       1     0  args   [Ljava/lang/String;MethodParameters:Name                           Flagsargs
}
SourceFile: "LockSyncDemo.java"

synchronized同步代码块

  • 使用的是monitorenter和monitorexit指令

    • public class LockSyncDemo {Object object = new Object();public void m1() {synchronized (object) {System.out.println("----hello synchronized code block");}}public static void main(String[] args) {}
      }
      

在这里插入图片描述

  • 一般是一个monitorenter,两个monitorexit

  • public class LockSyncDemo {Object object = new Object();public void m1() {synchronized (object) {System.out.println("----hello synchronized code block");throw new RuntimeException("-----exp");}}public static void main(String[] args) {}
    }
    
  • 如果直接在同步方法里面抛异常,就只有一个monitorexit(极端情况)

-在这里插入图片描述

synchronized普通同步方法

  • 调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程会将现持有monitor锁,然后再执行该方法,最后在方法完成(无论是否正常结束)时释放monitor
    -在这里插入图片描述

synchronized静态同步方法

  • ACC_STATICACC_SYNCHRONIZED访问标志区分该方法是否是静态同步方法
    在这里插入图片描述

反编译synchronized锁的是什么

  • 管程(Monitors,也称为监视器)是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变量。对共享变量能够进行的所有操作集中在一个模块中。(把信号量及其操作原语“封装”在一个对象内部)管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。管程提供了一种机制,管程可以看做一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制
  • 方法级的同步是隐式的,无须通过字节码指令来控制,它实现在方法调用和返回操作之中。虚拟机可以从方法常量池中的方法表结构中的ACC SYNCHRONIZED访问标志得知一个方法是否被声明为同步方法。当方法调用时,调用指令将会检查方法的ACC SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程。在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的管程将在异常抛到同步方法边界之外时自动释放。

为什么任何一个对象都可以成为一个锁?

在这里插入图片描述

原因:每个类都继承自Object,每个对象天生都带着一个对象监视器ObjectMonitor,每一个锁住的对象都会和Monitor关联

源码分析

C++源码:ObjectMonitor.java—>ObjectMonitor.cpp—>ObjectMonitor.hpp

总结:指针指向Monitor对象(也称为管程或监视器)的真实地址。每个对象都存在着一个monitor与之关联,当一个monitor被某个线程持有后,它便处于锁定状态。在Java虚拟机(HotSpot)中,monitor是由OnjectMonitor实现的,其主要的数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现):

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

_owner记录现在哪个线程持有了当前对象

在这里插入图片描述

在这里插入图片描述

公平锁和非公平锁

何为公平锁/非公平锁

  • 公平锁:是指多个线程按照申请锁的顺序来获取锁,这里类似于排队买票,先来的人先买,后来的人在队尾排着,是公平的。Lock lock = new ReentrantLock(true)表示公平锁。
  • 非公平锁:是指多个线程获取锁的顺序并不是按照申请的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级反转或者饥饿的状态(某个线程一直得不到锁)。Lock lock = new ReentrantLock(false)表示非公平锁,后来的线程也可能先获得锁,默认为非公平锁。
/*** 资源类,模拟3个售票员卖完50张票*/
class Ticket {private int number = 50;/*** 非公平锁*/ReentrantLock lock = new ReentrantLock();public void sale() {lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出第:\t" + (number--) + "\t 还剩下:" + number);}} finally {lock.unlock();}}
}public class SaleTicketDemo {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() -> {for (int i = 0; i < 55; i++) ticket.sale();}, "a").start();new Thread(() -> {for (int i = 0; i < 55; i++) ticket.sale();}, "b").start();new Thread(() -> {for (int i = 0; i < 55; i++) ticket.sale();}, "c").start();}
}

票全被a线程卖了,如果是公平锁,会一人卖一部分

在这里插入图片描述

ReentrantLock lock = new ReentrantLock(true);出入参数true,就变成了公平锁,这时候获得锁的线程是比较均匀的

在这里插入图片描述

面试题

  • 为什么会有公平锁/非公平锁的设计?为什么默认非公平?
    • 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分地利用CPU的时间片,尽量减少CPU空间状态时间
    • 使用多线程很重要的考量点是线程切换的开销(公平锁,线程切换频繁),当采用非公平锁时,当一个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得很大,所以就减少了线程的开销。
  • 什么时候用公平?什么时候用非公平?
    • 如果为了更高的吞吐量,很显然非公平锁是比较合适的,因为节省了很多线程切换的时间,吞吐量自然就上去了;否则就用公平锁。

预埋伏AQS

抽象队列同步器

在这里插入图片描述

在这里插入图片描述

后续深入分析

可重入锁(递归锁)

概念说明

  • 指在同一线程在外层方法获取到锁的时侯,在进入该线程的内层方法会自动获取锁(前提,锁的是同一个对象),不会因为之前已经获取过还没释放而阻塞
  • 所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个**优点是可一定程度避免死锁。**假如不是可重入锁,若有1个由synchronized修饰的递归调用方法,程序第2次调用该方法就被自己阻塞了

在这里插入图片描述

可重入锁种类

隐式锁(即synchronized关键字使用的锁)

  • 在一个synchronized修饰的方法或者代码块的内部调用本类的其他synchronized修饰的方法或者代码块时,永远可以得到锁。

【test1】

public class ReEntryLockDemo {public static void main(String[] args) {final Object o = new Object();new Thread(() -> {synchronized (o) {System.out.println("---------------外层调用");synchronized (o) {System.out.println("---------------中层调用");synchronized (o) {System.out.println("---------------内层调用");}}}}, "t1").start();}
}

输出

---------------外层调用
---------------中层调用
---------------内层调用

【test2】

public class ReEntryLockDemo {public synchronized void m1() {//指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。System.out.println(Thread.currentThread().getName() + "\t ----come in");m2();System.out.println(Thread.currentThread().getName() + "\t ----end m1");}public synchronized void m2() {System.out.println(Thread.currentThread().getName() + "\t ----come in");m3();}public synchronized void m3() {System.out.println(Thread.currentThread().getName() + "\t ----come in");}static Lock lock = new ReentrantLock();public static void main(String[] args) {ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();new Thread(() -> {reEntryLockDemo.m1();},"t1").start();}}

不会死锁,且从头到尾只有一个线程

在这里插入图片描述

synchronized可重入原理分析

在这里插入图片描述

每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针

  • 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1
  • 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么JVM可以将其计数器加1,否则需要等待,直至持有线程释放该锁
  • 当执行monitorexit时,JVM 则需将锁对象的计数器减1。计数器为零代表锁已被释放

显式锁(如ReentrantLock)

显式锁需要人为调用lock方法来获得锁

 /*** 注意:加锁几次就需要解锁几次* ---------------外层调用* ---------------中层调用* ---------------内层调用*/
Lock lock = new ReentrantLock();
new Thread(() -> {lock.lock();try {System.out.println("---------------外层调用");lock.lock();try {System.out.println("---------------中层调用");lock.lock();try {System.out.println("---------------内层调用");} finally {lock.unlock();}} finally {lock.unlock();}} finally {lock.unlock();}
}, "t2").start();

运行结果如下,说明ReentrantLock也是可重入锁

在这里插入图片描述

如果说unlock次数少于lock,最后线程1加的锁就没有完全解开,其他线程获取不到锁

public static void main(String[] args) {Lock lock = new ReentrantLock();new Thread(() -> {lock.lock();try {System.out.println("---------------外层调用");lock.lock();try {System.out.println("---------------中层调用");lock.lock();try {System.out.println("---------------内层调用");} finally {
//                        lock.unlock();}} finally {lock.unlock();}} finally {lock.unlock();}}, "t2").start();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t ----come in外层调用");} finally {lock.unlock();}}, "t2").start();}

在这里插入图片描述

死锁及排查

概念

死锁是指两个或两个以上的线程在执行过程中,因抢夺资源而造成的一种互相等待的现象,若无外力干涉,则它们无法再继续推进下去。

产生原因

  • 系统资源不足
  • 进程运行推进顺序不合适
  • 系统资源分配不当

死锁案例

  • 线程A:持有锁A,想要锁B
  • 线程B:持有锁B,想要锁A
  • 两个线程,不再是可重入锁

在这里插入图片描述

public class DeadLockDemo {public static void main(String[] args) {final Object objectA = new Object();final Object objectB = new Object();new Thread(() -> {synchronized (objectA) {System.out.println(Thread.currentThread().getName() + "\t 自己持有A锁,希望获得B锁");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (objectB) {System.out.println(Thread.currentThread().getName() + "\t 成功获得B锁");}}}, "A").start();new Thread(() -> {synchronized (objectB) {System.out.println(Thread.currentThread().getName() + "\t 自己持有B锁,希望获得A锁");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (objectA) {System.out.println(Thread.currentThread().getName() + "\t 成功获得A锁");}}}, "B").start();}
}

发生死锁

在这里插入图片描述

如何排查死锁

纯命令

  • jps -l:查出正在运行的进程编号
  • jstack 进程编号:查看正在死锁的进程的信息
PS D:\Projects\juc_bilibili\target\classes\com\bilibili\juc\locks> jps -l
18608 com.bilibili.juc.locks.DeadLockDemo
19680 com.bilibili.juc.locks.ReEntryLockDemo
6132 org.jetbrains.jps.cmdline.Launcher
1144
32040 jdk.jcmd/sun.tools.jps.Jps
29452 org.jetbrains.idea.maven.server.RemoteMavenServer36
PS D:\Projects\juc_bilibili\target\classes\com\bilibili\juc\locks> jstack 18608
2024-07-17 10:05:45
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.361-b09 mixed mode):"DestroyJavaVM" #25 prio=5 os_prio=0 tid=0x000002d62b66b000 nid=0x7ea8 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"B" #24 prio=5 os_prio=0 tid=0x000002d667d07000 nid=0x4c5c waiting for monitor entry [0x000000f9aa8ff000]java.lang.Thread.State: BLOCKED (on object monitor)at com.bilibili.juc.locks.DeadLockDemo.lambda$main$1(DeadLockDemo.java:37)- waiting to lock <0x000000066b975db0> (a java.lang.Object)- locked <0x000000066b975dc0> (a java.lang.Object)at com.bilibili.juc.locks.DeadLockDemo$$Lambda$2/2046562095.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)"A" #23 prio=5 os_prio=0 tid=0x000002d667cfc000 nid=0x276c waiting for monitor entry [0x000000f9aa7ff000]java.lang.Thread.State: BLOCKED (on object monitor)at com.bilibili.juc.locks.DeadLockDemo.lambda$main$0(DeadLockDemo.java:23)- waiting to lock <0x000000066b975dc0> (a java.lang.Object)- locked <0x000000066b975db0> (a java.lang.Object)at com.bilibili.juc.locks.DeadLockDemo$$Lambda$1/1706234378.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)"Service Thread" #22 daemon prio=9 os_prio=0 tid=0x000002d667a19800 nid=0x76ec runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread14" #21 daemon prio=9 os_prio=2 tid=0x000002d66796f000 nid=0x7d54 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread13" #20 daemon prio=9 os_prio=2 tid=0x000002d667973800 nid=0x7b14 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread12" #19 daemon prio=9 os_prio=2 tid=0x000002d667970800 nid=0x6e24 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread11" #18 daemon prio=9 os_prio=2 tid=0x000002d667972000 nid=0x6fcc waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread10" #17 daemon prio=9 os_prio=2 tid=0x000002d66796e000 nid=0x7d40 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread9" #16 daemon prio=9 os_prio=2 tid=0x000002d66796d800 nid=0x4c24 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread8" #15 daemon prio=9 os_prio=2 tid=0x000002d667971000 nid=0x46fc waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread7" #14 daemon prio=9 os_prio=2 tid=0x000002d66795c000 nid=0x7a8c waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread6" #13 daemon prio=9 os_prio=2 tid=0x000002d667945000 nid=0x71f4 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread5" #12 daemon prio=9 os_prio=2 tid=0x000002d66792c000 nid=0x7e88 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread4" #11 daemon prio=9 os_prio=2 tid=0x000002d6653bc000 nid=0x6d48 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000002d6653af800 nid=0x7878 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000002d6678fa000 nid=0x7d48 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000002d6678f9000 nid=0x69ec waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000002d6678f6800 nid=0x4f14 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000002d665380000 nid=0x3dac runnable [0x000000f9a95fe000]java.lang.Thread.State: RUNNABLEat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)at java.net.SocketInputStream.read(SocketInputStream.java:171)at java.net.SocketInputStream.read(SocketInputStream.java:141)at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)- locked <0x000000066bce6df0> (a java.io.InputStreamReader)at java.io.InputStreamReader.read(InputStreamReader.java:184)at java.io.BufferedReader.fill(BufferedReader.java:161)at java.io.BufferedReader.readLine(BufferedReader.java:324)- locked <0x000000066bce6df0> (a java.io.InputStreamReader)at java.io.BufferedReader.readLine(BufferedReader.java:389)at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:53)"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000002d6651b5000 nid=0x68a8 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000002d66515e800 nid=0x7158 runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000002d66282d800 nid=0x7934 in Object.wait() [0x000000f9a92ff000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x000000066b788f08> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:150)- locked <0x000000066b788f08> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:171)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:188)"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000002d66514c000 nid=0x7c58 in Object.wait() [0x000000f9a91ff000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x000000066b786ba0> (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:502)at java.lang.ref.Reference.tryHandlePending(Reference.java:191)- locked <0x000000066b786ba0> (a java.lang.ref.Reference$Lock)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)"VM Thread" os_prio=2 tid=0x000002d62b714800 nid=0x6b14 runnable"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000002d62b680000 nid=0x79b8 runnable"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000002d62b681800 nid=0x7ef0 runnable"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000002d62b683000 nid=0x7894 runnable"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000002d62b685800 nid=0x2824 runnable"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000002d62b687800 nid=0x2bb4 runnable"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x000002d62b688800 nid=0x7c44 runnable"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000002d62b68b800 nid=0x79b4 runnable"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000002d62b68c000 nid=0x26f0 runnable"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x000002d62b68d000 nid=0x7c94 runnable"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x000002d62b68e000 nid=0x7fdc runnable"GC task thread#10 (ParallelGC)" os_prio=0 tid=0x000002d62b68f800 nid=0x7edc runnable"GC task thread#11 (ParallelGC)" os_prio=0 tid=0x000002d62b693800 nid=0x3814 runnable"GC task thread#12 (ParallelGC)" os_prio=0 tid=0x000002d62b694800 nid=0x1a4c runnable"GC task thread#13 (ParallelGC)" os_prio=0 tid=0x000002d62b697800 nid=0x790c runnable"GC task thread#14 (ParallelGC)" os_prio=0 tid=0x000002d62b698800 nid=0x7d64 runnable"GC task thread#15 (ParallelGC)" os_prio=0 tid=0x000002d62b699800 nid=0x3cb4 runnable"GC task thread#16 (ParallelGC)" os_prio=0 tid=0x000002d62b69c000 nid=0x7fb0 runnable"GC task thread#17 (ParallelGC)" os_prio=0 tid=0x000002d62b6a0800 nid=0x79ec runnable"GC task thread#18 (ParallelGC)" os_prio=0 tid=0x000002d62b69f800 nid=0x7020 runnable"A":waiting to lock monitor 0x000002d66282d418 (object 0x000000066b975dc0, a java.lang.Object),which is held by "B"Java stack information for the threads listed above:
===================================================
"B":at com.bilibili.juc.locks.DeadLockDemo.lambda$main$1(DeadLockDemo.java:37)- waiting to lock <0x000000066b975db0> (a java.lang.Object)- locked <0x000000066b975dc0> (a java.lang.Object)at com.bilibili.juc.locks.DeadLockDemo$$Lambda$2/2046562095.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)
"A":at com.bilibili.juc.locks.DeadLockDemo.lambda$main$0(DeadLockDemo.java:23)- waiting to lock <0x000000066b975dc0> (a java.lang.Object)- locked <0x000000066b975db0> (a java.lang.Object)at com.bilibili.juc.locks.DeadLockDemo$$Lambda$1/1706234378.run(Unknown Source)at java.lang.Thread.run(Thread.java:750)Found 1 deadlock.

在这里插入图片描述

图形化

  • jconsole:cmd执行

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

写锁(独占锁)/读锁(共享锁)

深度源码分析见后面

自旋锁spinLock

深度源码分析见后面

无锁->独占锁->读写锁->邮戳锁

深度源码分析见后面

无锁->偏向锁->轻量锁->重量锁

深度源码分析见后面

文章说明

该文章是本人学习 尚硅谷 的学习笔记,文章中大部分内容来源于 尚硅谷 的视频尚硅谷JUC并发编程(对标阿里P6-P7),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对 尚硅谷 的优质课程表示感谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3269995.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

c++入门----类与对象(中)

OK呀&#xff0c;家人们承接上文&#xff0c;当大家看过鄙人的上一篇博客后&#xff0c;我相信大家对我们的c已经有一点印象了。那么我们现在趁热打铁再深入的学习c入门的一些知识。 类的默认成员函数 首先我们学习的是我们的默认函数。不知道大家刚读这个名词是什么反应。默认…

鱼哥好书分享活动第27期:看完这篇《云原生安全》了解云原生环境安全攻防实战技巧!

鱼哥好书分享活动第27期&#xff1a;看完这篇《云原生安全》了解云原生安全攻防实战技巧&#xff01; 主要内容&#xff1a;读者对象&#xff1a;本书目录&#xff1a;了解更多&#xff1a;赠书抽奖规则: 当前全球数字化的发展逐步进入深水区&#xff0c;云计算模式已经广泛应用…

【Linux C | 网络编程】简易进程池的实现详解(一)

进程池&#xff08;Process Pool&#xff09;是一种并发编程的模型&#xff0c;用于管理和复用多个进程&#xff0c;以提高系统的效率和性能。它主要解决的问题是减少因频繁创建和销毁进程而带来的性能开销&#xff0c;特别是在需要处理大量并发任务时尤为有效。 主要组成部分…

法律 | 法律人AI使用指南

原文&#xff1a;法律 | 法律人AI使用指南|法官|法院|文书|公司法_网易订阅 01 引言 过去半年多&#xff0c;我一直在尝试着用AI来辅助自己的各项法律工作&#xff0c;将AI融入自己的日常工作之中&#xff0c;并试图形成自身稳定的“法律AI”工作流。在此过程中&#xff0c;…

实时捕获数据库变更

1.CDC概述 CDC 的全称是 Change Data Capture &#xff0c;在广义的概念上&#xff0c;只要能捕获数据变更的技术&#xff0c;我们都可以称为 CDC 。我们目前通常描述的CDC 技术主要面向数据库的变更&#xff0c;是一种用于捕获数据库中数据变更的技术&#xff0c;CDC 技术应用…

【Plotly-驯化】一文教你学会画最美动态可视化的热力图:heatmap技巧

【Plotly-驯化】一文教你学会画最美动态可视化的热力图&#xff1a;heatmap技巧 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取…

JS:JavaScript 简介

前言 在当今数字时代&#xff0c;JavaScript已然成为了现代Web开发的基石。从最初作为浏览器中的一个小型脚本语言&#xff0c;到如今成为驱动全球互联网的强大引擎&#xff0c;JavaScript的发展历程本身就是一个令人着迷的故事。 好了开始学习JS吧 1. 如果刚开始了解js&#…

智慧城管解决方案

1. 项目整体概述 智慧城管项目面临历史发展机遇&#xff0c;十九大提出以人为核心保障民生&#xff0c;推进新型城镇化。市民对政府服务有新诉求&#xff0c;同时云计算、物联网、移动互联网等技术迅速发展。 2. 传统城管业务模式问题 传统城管业务模式存在问题&#xff0c;…

leetcode106. 从中序与后序遍历序列构造二叉树,力扣105姊妹题

leetcode106. 从中序与后序遍历序列构造二叉树 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7…

【每日刷题】Day86

【每日刷题】Day86 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 118. 杨辉三角 - 力扣&#xff08;LeetCode&#xff09; 2. 数组中出现次数超过一半的数字_牛客题霸…

详解Mysql InnoDB引擎 04

文章目录 1. InnoDB 简介2. 逻辑存储结构2.1 表空间 idb文件2.2 段2.3 区 1M2.4 页 16KB2.5 行 3. 架构3.1 内存结构3.1.1 Buffer Pool 缓冲池3.1.2 Change Buffer 更改缓冲区3.1.3 Adaptive Hash Index3.1.4 Log Buffer 3.2 磁盘结构 4. 后台线程5. 事务原理5.1 redo log 重做…

C++的STL简介(一)

目录 1.什么是STL 2.STL的版本 3.STL的六大组件 4.string类 4.1为什么学习string类&#xff1f; 4.2string常见接口 4.2.1默认构造 ​编辑 4.2.2析构函数 Element access: 4.2.3 [] 4.2.4迭代器 ​编辑 auto 4.2.4.1 begin和end 4.2.4.2.regin和rend Capacity: 4.2.5…

LangChain曝关键漏洞,数百万AI应用面临攻击风险

LangChain是一个流行的开源生成式人工智能框架&#xff0c;其官网介绍&#xff0c;有超过一百万名开发者使用LangChain框架来开发大型语言模型&#xff08;LLM&#xff09;应用程序。LangChain的合作伙伴包括云计算、人工智能、数据库和其他技术开发领域的许多知名企业。 近日&…

前端模块化CommonJS、AMD、CMD、ES6

在前端开发中&#xff0c;模块化是一种重要的代码组织方式&#xff0c;它有助于将复杂的代码拆分成可管理的小块&#xff0c;提高代码的可维护性和可重用性。CommonJS、AMD&#xff08;异步模块定义&#xff09;和CMD&#xff08;通用模块定义&#xff09;是三种不同的模块规范…

数据库设计过程概述

1.首先进行需求分析&#xff0c;通过数据流图&#xff0c;数据字典&#xff0c;需求说明书等确定数据处理要求 2.概要结构设计 3.逻辑结构设计 4.物理设计

计算机二级题--结构体及链表 章节

之前写的有结构体全部的知识点&#xff0c;这一篇主要针对计算机二级真题的整理。 需要备考计算机二级的小伙伴们先收藏起来吧。整理不易&#xff0c;不过有帮助记得点赞哦 高频考点&#xff08;容易出错&#xff0c;附有例题&#xff09; 1.结构体传参&#xff0c;传值的区…

数字图像处理中的常用特殊矩阵及MATLAB实现详解

一、前言 Matlab的名称来源于“矩阵实验室&#xff08;Matrix Laboratory&#xff09;”&#xff0c;其对矩阵的操作具有先天性的优势&#xff08;特别是相对于C语言的数组来说&#xff09;。在数字图像处理中&#xff0c;为了提高编程效率&#xff0c;我们可以使用多种方式来创…

MATLAB基础:函数与函数控制语句

今天我们继续学习Matlab中函数相关知识。 API的查询和调用 help 命令是最基本的查询方法&#xff0c;可查询所有目录、指定目录、命令、函数。 我们直接点击帮助菜单即可查询所需的API函数。 lookfor 关键字用于搜索相关的命令和函数。 如&#xff0c;我们输入lookfor inpu…

JDK的配置

安装好JDK后&#xff0c;配置三个环境变量 第一步&#xff0c;配置JAVA_HOME. 先找到JDK的安装目录&#xff0c;然后复制路径&#xff0c;在电脑的环境变量里增添变量名为JAVA_HOME,变量值为 C:\Program Files\Java\jdk1.8.0_192。&#xff08;具体根据你的JDK安装路径&…

使用flutter做圆形进度条 (桌面端)

前言 最近收到一个需求&#xff0c;需要使用flutter 来做一个圆形进度条&#xff0c;这可难倒我了&#xff0c;毕竟我是做前端的&#xff0c;flutter 之前接触的也少&#xff0c;但没办法&#xff0c;既然需求有了&#xff0c;也得硬着头皮上了&#xff0c;先来看看做的效果。…