• Java中的锁


    1. ReentrantLock 与 synchronized有什么区别?

            相同点:两者都是同步锁、可重入锁,只有获得该锁的使用权后,线程才能使用否则将会进行等待,并且由于是可重入锁,JVM会允许同一个线程重复的获取同一种锁。

            不同点:synchronized是一个关键字,依赖于JVM底层实现,在给代码块或实例方法加锁时,使用this对象作为锁,给静态方法加锁时,使用当前对象的class类作为锁,所以在底层是通过对象内部的monitor监视器实现,线程通过执行monitorenter/monitorexit指令尝试获取monitor的所有权,当monitor被占用时就会处于锁定状态。获取锁是通过线程抢占的形式,并且只支持非公平锁,在方法执行结束自动释放锁。

            ReentrantLock实现了Lock接口,在获取锁、释放锁时必须手动使用lock()、unlock()方法,并且支持公平锁和非公平锁,默认情况下使用非公平锁,相比于synchronized,可以通过tryLock()尝试获取锁,性能更安全,并且提供了大量更高级的并发功能,能大大简化多线程程序的编写。

    2. sleep()和wait()有什么区别?

            两者都可以暂停线程的执行。但最主要的区别在于:sleep方法没有释放锁,而wait方法释放了锁。

            wait()通常被用于线程间的交互/通信,当调用wait()方法后,线程不会自动苏醒,需要别的线程调用同一个对象的notify()或者notifyAll()方法。或者wait(long timeout)超时后线程自动苏醒。

            sleep()通常用于线程暂停/休眠,sleep()方法执行完成后,线程会自动苏醒。

    3. 乐观锁和悲观锁是什么?

            悲观锁 (Pessimistic Locking),具有强烈的独占和排他特性。它指的是对数据被外界修改持保守态度。因此,在整个执行过程中,将处于锁定状态。所以,悲观锁是一种悲观思想,它总认为最坏的情况可能会出现,它认为数据很可能会被其他人所修改,所以悲观锁在持有数据的时候总会把资源 或者 数据 锁住,这样其他线程想要请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止。Java中的Synchronized和ReentrantLock是一种悲观锁思想的实现,因为Synchronized和ReentrantLock不管是否持有资源,它都会尝试去加锁。

            乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过。Java中的StampedLock和AtomicInteger是一种乐观锁思想的实现。

    4. 详细说一下sychronized如何实现的?锁升级的过程了解吗?

            synchronized是一个关键字,依赖于JVM底层实现,在给代码块或实例方法加锁时,使用this对象作为锁,给静态方法加锁时,使用当前对象的class类作为锁,所以在底层是通过对象内部的monitor监视器实现,线程通过执行monitorenter/monitorexit指令尝试获取monitor的所有权,当monitor被占用时就会处于锁定状态。   

    • 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者,代表持有锁;
    • 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;
    • 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

            在JVM底层的锁有三种锁:偏向锁、轻量级锁、重量级锁

            偏向锁是在单线程的情况下自动获得的锁,当有多个线程不是在并发条件下执行,会升级成轻量级锁,如果有并发执行再升级成重量级锁。

            在升级成轻量级锁的过程中,JVM虚拟机会在当前线程的栈帧中建立一个Lock Record锁记录,存放当前对象锁的Mark Word的拷贝,拷贝成功后,会将当前对象所的Mark Word中的ptr_to_lock_record更新为指向Lock Record的指针,并将Lock Record里的owner指针指向到对象的Mark Word。如果更新成功,代表升级成轻量级锁,并将对象Mark Wordlock标志位设置为00.如果失败,检查对象的Mark Word是否已经指向当前线程的栈帧,如果指向,则继续执行,否则,标志位变成10,升级成重量级锁,其他等待线程阻塞,当前线程自旋尝试获取锁。


    5. 产生死锁的四个条件是什么? 

    • 资源互斥:对所分配的资源进行排它性控制,锁在同一时刻只能被一个线程使用;
    • 不可剥夺:线程已获得的资源在未使用完之前,不能被剥夺,只能等待占有者自行释放锁;
    • 请求等待:当线程因请求资源而阻塞时,对已获得的资源保持不放。
    • 循环等待:线程之间的相互等待。
  • 相关阅读:
    Vue 2.0 与 Vue 3.0 的主要差异
    线段树——维护序列(两个懒标记的情况)
    Ubuntu上安装配置Nginx
    下载git
    zookeeper核心源码分析
    RHCE第一天练习题
    .NET 8 Release Candidate 1 (RC1)现已发布,包括许多针对ASP.NET Core的重要改进!
    Python之面向对象(二)
    代码扫描搭建Sonar+docker+jenkins
    iOS之卡顿检测
  • 原文地址:https://blog.csdn.net/qq_45958440/article/details/126858170