【JUC】Java锁介绍

news2024/11/13 22:08:58

文章目录

  • 阿里锁开发规范
  • 乐观锁和悲观锁
    • 悲观锁
    • 乐观锁
  • 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_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class java/lang/Object
       8: dup
       9: invokespecial #1                  // Method java/lang/Object."<init>":()V
      12: putfield      #3                  // Field object:Ljava/lang/Object;
      15: return

  public void m1();
    Code:
       0: aload_0
       1: getfield      #3                  // Field object:Ljava/lang/Object;
       4: dup
       5: astore_1
       6: monitorenter
       7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: ldc           #5                  // String ----hello synchronized code block
      12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: new           #7                  // class java/lang/RuntimeException
      18: dup
      19: ldc           #8                  // String -----exp
      21: invokespecial #9                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
      24: athrow
      25: astore_2
      26: aload_1
      27: monitorexit
      28: aload_2
      29: athrow
    Exception table:
       from    to  target type
           7    28    25   any

  public synchronized void m2();
    Code:
       0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #10                 // String ----hello synchronized m2
       5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

  public static synchronized void m3();
    Code:
       0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #11                 // String ----hello static synchronized m3
       5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

  public 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.class
  Last modified 2024年7月16日; size 1125 bytes
  SHA-256 checksum 705394b1f660417439abfb88585f1fc108b5caf625d01bc4fbfa19a8f9aad705
  Compiled from "LockSyncDemo.java"
public class com.bilibili.juc.locks.LockSyncDemo
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #12                         // com/bilibili/juc/locks/LockSyncDemo
  super_class: #2                         // java/lang/Object
  interfaces: 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: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: new           #2                  // class java/lang/Object
         8: dup
         9: invokespecial #1                  // Method java/lang/Object."<init>":()V
        12: putfield      #3                  // Field object:Ljava/lang/Object;
        15: return
      LineNumberTable:
        line 10: 0
        line 11: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  this   Lcom/bilibili/juc/locks/LockSyncDemo;

  public void m1();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: getfield      #3                  // Field object:Ljava/lang/Object;
         4: dup
         5: astore_1
         6: monitorenter
         7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #5                  // String ----hello synchronized code block
        12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: new           #7                  // class java/lang/RuntimeException
        18: dup
        19: ldc           #8                  // String -----exp
        21: invokespecial #9                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
        24: athrow
        25: astore_2
        26: aload_1
        27: monitorexit
        28: aload_2
        29: athrow
      Exception table:
         from    to  target type
             7    28    25   any
      LineNumberTable:
        line 14: 0
        line 15: 7
        line 16: 15
        line 17: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      30     0  this   Lcom/bilibili/juc/locks/LockSyncDemo;
      StackMapTable: number_of_entries = 1
        frame_type = 255 /* full_frame */
          offset_delta = 25
          locals = [ class com/bilibili/juc/locks/LockSyncDemo, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]

  public synchronized void m2();
    descriptor: ()V
    flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #10                 // String ----hello synchronized m2
         5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 21: 0
        line 22: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/bilibili/juc/locks/LockSyncDemo;

  public static synchronized void m3();
    descriptor: ()V
    flags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #11                 // String ----hello static synchronized m3
         5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 25: 0
        line 26: 8

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 31: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      args
}
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: RUNNABLE
        at 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://www.coloradmin.cn/o/1950869.html

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

相关文章

【Python机器学习】决策树的构造——递归构建决策树

我们可以采用递归的原则处理数据集&#xff0c;递归结束的条件是&#xff1a;程序遍历完所有划分数据集的属性&#xff0c;或者每个分支下的所有实例都具有相同的分类。如果所有实例具有相同的分类&#xff0c;则得到一个叶子节点或者终止块。任何到达叶子节点的数据必然属于叶…

软考:软件设计师 — 7.软件工程

七. 软件工程 1. 软件工程概述 &#xff08;1&#xff09;软件生存周期 &#xff08;2&#xff09;软件过程 软件开发中所遵循的路线图称为 "软件过程"。 针对管理软件开发的整个过程&#xff0c;提出了两个模型&#xff1a;能力成熟度模型&#xff08;CMM&#…

uniapp引入自定义图标

目录 一、选择图标&#xff0c;加入购物车 二、下载到本地 三、导入项目 四、修改字体引用路径 五、开始使用 这里以扩展iconfont图标为例 官网&#xff1a;iconfont-阿里巴巴矢量图标库 一、选择图标&#xff0c;加入购物车 二、下载到本地 直接点击下载素材&#xff0…

mysql中You can’t specify target table for update in FROM clause错误

mysql中You can’t specify target table for update in FROM clause错误 You cannot update a table and select directly from the same table in a subquery. mysql官网中有这句话&#xff0c;我们不能在一个语句中先在子查询中从某张表查出一些值&#xff0c;再update这张表…

matplotlib 画图函数,最常用的

并排显示2个图片 import os import numpy as np from PIL import Image import matplotlib.pyplot as pltimage1 Image.open(a.png) image2 Image.open(a2.png)# Create a figure with two subplots (1 row, 2 columns) fig, axes plt.subplots(1, 2, figsize(10, 5))# Di…

使用Log4Net和中间件记录接口访问日志

一、功能概述 Log4Net log4net 是一个用于.NET应用程序的日志记录框架。它允许开发人员在他们的应用程序中记录各种信息&#xff0c;以便追踪应用程序的运行状态、排查问题和分析性能。log4net的主要作用包括&#xff1a; 日志记录: 记录应用程序的运行时信息&#xff0c;如调…

【单片机毕业设计选题24081】-路灯无线数据采集器

系统功能: 手机开启2.4G WiFi热点后再给系统上电 系统操作说明&#xff1a; 上电后OLED显示 “欢迎使用智能路灯系统请稍后”&#xff0c;两秒后显示Connecting...表示 正在连接阿里云&#xff0c;正常连接阿里云后显示第一页面&#xff0c;如长时间显示Connecting...请 检…

复现open-mmlab的mmsegmentation详细细节

复现open-mmlab的mmsegmentation详细细节 1.配置环境2.数据处理3.训练 1.配置环境 stage1&#xff1a;创建python环境 conda create --name openmmlab python3.8 -y conda activate openmmlabstage2&#xff1a;安装pytorch&#xff08;这里我是以torch1.10.0为例&#xff09…

昇思25天学习打卡营第22天|Pix2Pix实现图像转换

Pix2Pix图像转换学习总结 概述 Pix2Pix是一种基于条件生成对抗网络&#xff08;cGAN&#xff09;的深度学习模型&#xff0c;旨在实现不同图像风格之间的转换&#xff0c;如从语义标签到真实图像、灰度图到彩色图、航拍图到地图等。这一模型由Phillip Isola等人在2017年提出&…

《0基础》学习Python——第二十四讲__爬虫/<7>深度爬取

一、深度爬取 深度爬取是指在网络爬虫中&#xff0c;获取网页上的所有链接并递归地访问这些链接&#xff0c;以获取更深层次的页面数据。 通常&#xff0c;一个简单的爬虫只会获取到初始页面上的链接&#xff0c;并不会进一步访问这些链接上的其他页面。而深度爬取则会不断地获…

vue3 父组件 props 异步传值,子组件接收不到或接收错误

1. 使用场景 我们在子组件中通常需要调用父组件的数据&#xff0c;此时需要使用 vue3 的 props 进行父子组件通信传值。 2. 问题描述 那么此时问题来了&#xff0c;在使用 props 进行父子组件通信时&#xff0c;因为数据传递是异步的&#xff0c;导致子组件无法成功获取数据…

idea设置类注释模板作者、日期、描述等信息

文章目录 前言一、新建类的时候自动添加类注释1.打开设置2.模版配置示例如下3.实际生成效果 前言 由于每次换电脑时都需要重新对idea进行设置&#xff0c;为了方便大家的开发配置&#xff0c;同时也为自己以后配置留一份记录&#xff08;毕竟每次换环境都需要重新配置一遍&…

DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null

文章目录 一、报错内容二、原因三、DB2中的VARCHAR(100)类型能存储多少汉字&#xff1f; 一、报错内容 Cause: com.ibm.db2.jcc.am.mo: DB2 SQL Error: SQLCODE-302, SQLSTATE22001, SQLERRMCnull, DRIVER3.58.82 ; DB2 SQL Error: SQLCODE-302, SQLSTATE22001, SQLERRMCnull,…

Aider + Llama 3.1:无需编码开发全栈APP

Llama 3.1在代码生成方面的卓越表现 在代码生成领域&#xff0c;Llama 3.1的表现尤为出色&#xff0c;几乎成为了开源模型中的佼佼者。它不仅在代码自动化和生成方面表现突出&#xff0c;还可以作为AI编程助手&#xff0c;帮助调试代码和开发完整的应用程序。在多个基准测试中…

【用最少数量的箭引爆气球】python刷题记录

R2-贪心篇. 求最小&#xff0c;那就尽可能地假设更多的气球y值不相同咯。 不对&#xff0c;气球除了y值我们随便摆&#xff0c;所以找尽可能多重叠的&#xff0c;就作为同一只箭。 class Solution:def findMinArrowShots(self, points: List[List[int]]) -> int:#贪心策略…

JavaScript关键词

JavaScript 关键词 JavaScript 语句常常通过某个关键词来标识需要执行的 JavaScript 动作。 下面的表格列出了一部分将在教程中学到的关键词&#xff1a; 关键词 描述 break 终止 switch 或循环。 continue 跳出循环并在顶端开始。 debugger 停止执行 JavaScript&…

powershell自定义命令别名

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、查看命令别名二、常见的别名三、自定义别名1.GUI编辑2.命令行编辑 总结 前言 有时候在windows上使用powershell时候常常苦于别名问题&#xff0c;像我这样…

Windows系统下cython_bbox库的正确安装步骤最简单方法

​ 最近做的项目需要安装cython_bbox包&#xff0c;但是当我天真的输入pip install cython_bbox准备满心欢喜的等待安装成功却发现…&#xff0c;它报错了&#xff0c;这是一个从未见过的、非常长的报错&#xff0c;它长这个样子&#xff1a; ​​ 因此不能直接通过pip安装&am…

每日任务:TCP/IP模型和OSI模型的区别

介绍一下TCP/IP模型和OSI模型的区别&#xff1f; OSI模型由国标准化组织提出&#xff0c;而TCP/IP模型是由美国国防部开发的&#xff1b; OSI模型由七个层次组成&#xff0c;从下到上依次为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。而TCP/IP模型只有四层…

心动小站Ⅸ--Nvidia一种夸张的增长

当有灭绝级别的威胁时&#xff0c;你希望它如何出现在头版&#xff1f;华尔街日报或 CNN 的新闻中说它即将发生&#xff1f; 大多数新闻都是在灾难发生后才传到你耳中的。谈到人工智能&#xff0c;我们已经收到了很多警告&#xff0c;这些警告来自各个领域的专家&#xff0c;比…