找回密码
 立即注册
首页 业界区 业界 JUC: 线程锁

JUC: 线程锁

拼潦 10 小时前
1 面试题复盘


  • 如何理解多线程,如何处理并发,线程池有哪些核心参数?
  • Java加锁有哪几种锁?
  • synchronized原理是什么?为什么可重入?如何获取对象的锁?
  • JVM对原生锁做了哪些优化?
  • 什么是锁清除和锁粗化?
  • 乐观锁是什么?synchronized与乐观锁什么区别?
  • volatile有什么作用?
  • ReentrantLock原理是什么?
  • AQS框架原理介绍一下?
  • 简单说说Lock
  • 是否使用过CountDownLanch? 如何使用?
2 乐观锁与悲观锁

(1)悲观锁

synchronized和Lock都是悲观锁, 同一时间点,有且只有一个线程能够访问对应的资源。 写操作多的场景使用。
(2)乐观锁

认为自己在使用数据时,不会有别的线程修改数据或资源,所以不会添加锁。只是在更新资源的时候,需要去判断当前数据有没有别的线程更新过。判断规则有:

  • 版本号机制version,每一次更新一个版本号。
  • 采用CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。比较并交换
3 锁是什么

(1)锁案例演示  - synchronized的三种应用方式
  1. // 1. 对象锁:对于非静态方法使用synchronized就是加的对象锁,获得的是这个对象(this)作为锁
  2. public synchronized void sentEmail(){
  3.     try {
  4.         TimeUnit.SECONDS.sleep(4);
  5.     } catch (InterruptedException e) {
  6.         throw new RuntimeException(e);
  7.     }
  8.     System.out.println("sent email");
  9. }
  10. // 2. 类锁:对于静态方法或使用synchronized就是加的类锁,获得的是这个类对象(.class)作为锁
  11. public static synchronized void sentSMS(){
  12.     System.out.println("sent SMS");
  13. }
  14. // 3. 代码块,使用的是synchronized括号内的对象
  15. public void testSynchronized(){
  16.     synchronized (this){
  17.         System.out.println("testSynchronized");
  18.     }
  19. }
复制代码
(2)从字节码角度分析synchronized实现


  • javap -c ***.class文件反编译
  1. javap -c a.class #         ​​反汇编代码​​,输出每个方法的 Java 字节码指令(指令集)
  2. -v或 -verbose # 输出​​最详细的附加信息​​,包括版本号、常量池、方法描述符(签名)、栈大小等
复制代码

  • synchronized同步代码块
  1. public void testSynchronized(){
  2.     synchronized (this){
  3.         System.out.println("testSynchronized");
  4.     }
  5. }
复制代码
  1. public void testSynchronized();
  2.     Code:
  3.        0: aload_0
  4.        1: dup
  5.        2: astore_1
  6.        3: monitorenter # 获取锁
  7.        4: getstatic     #26                 // Field java/lang/System.out:Ljava/io/PrintStream;
  8.        7: ldc           #44                 // String testSynchronized
  9.        9: invokevirtual #34                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  10.       12: aload_1
  11.       13: monitorexit # 释放锁
  12.       14: goto          22
  13.       17: astore_2
  14.       18: aload_1
  15.       19: monitorexit # 异常情况也可以释放锁
  16.       20: aload_2
  17.       21: athrow
  18.       22: return
  19.     Exception table:
  20.        from    to  target type
  21.            4    14    17   any
  22.           17    20    17   any
  23. }
复制代码

  • synchronized对象锁
-v
  1. public synchronized void sentEmail(){
  2.     System.out.println("sent email");
  3. }
复制代码
  1.   public synchronized void sentEmail();
  2.     descriptor: ()V
  3.     flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED # 会检查对象的ACC_SYNCHRONIZED是否被设置,如果设置了,则会持有monitor直到方法完成释放
  4.     Code:
  5.       stack=2, locals=1, args_size=1
  6.          0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
  7.          3: ldc           #13                 // String sent email
  8.          5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  9.          8: return
  10.       LineNumberTable:
  11.         line 31: 0
  12.         line 32: 8
  13.       LocalVariableTable:
  14.         Start  Length  Slot  Name   Signature
  15.             0       9     0  this   Lcom/thread/sgg/juc/Phone;
复制代码

  • synchronized类锁
  1. public static synchronized void sentSMS(){
  2.     System.out.println("sent SMS");
  3. }
复制代码
  1.   public static synchronized void sentSMS();
  2.     descriptor: ()V
  3.     flags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED # 根据是否有ACC_STATIC是否存在判断应该是类锁还是对象锁
  4.     Code:
  5.       stack=2, locals=0, args_size=0
  6.          0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
  7.          3: ldc           #21                 // String sent SMS
  8.          5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  9.          8: return
  10.       LineNumberTable:
  11.         line 34: 0
  12.         line 35: 8
复制代码
(3)反编译synchronized锁的是什么

为什么任何一个对象都可以成为一个锁?
Object是任何类的父类
什么是管程monitor?
monitor是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。
这些共享资源一般是硬件设备或一群变量。对共享变量能够进行的所有操作集中在一个模块中。(把信号量及其操作原语“封装”在一个对象内部)管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。管程提供了一种机制,管程可以看做一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制。
  1. //结构体如下
  2. ObjectMonitor::ObjectMonitor() {  
  3.   _header       = NULL;  
  4.   _count       = 0;   // 该线程获取锁的次数
  5.   _waiters      = 0,  
  6.   _recursions   = 0;       //线程的重入次数
  7.   _object       = NULL;  
  8.   _owner        = NULL;    //标识拥有该monitor的线程
  9.   _WaitSet      = NULL;    //等待线程组成的双向循环链表,_WaitSet是第一个节点
  10.   _WaitSetLock  = 0 ;  
  11.   _Responsible  = NULL ;  
  12.   _succ         = NULL ;  
  13.   _cxq          = NULL ;    //多线程竞争锁进入时的单向链表
  14.   FreeNext      = NULL ;  
  15.   _EntryList    = NULL ;    //_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点
  16.   _SpinFreq     = 0 ;  
  17.   _SpinClock    = 0 ;  
  18.   OwnerIsThread = 0 ;  
  19. }  
复制代码
4 公平锁与非公平锁

公平锁:多个线程按照申请锁的顺序获取锁
非公平锁:不按照顺序获取锁,可能导致某些线程处于饥饿状态
为什么有公平和非公平锁的设计?为什么默认非公平锁?
非公平锁能减少线程切换,减少CPU空闲状态的时间,效率较高。
  1. public class ReentrantLock implements Lock, java.io.Serializable {
  2.     abstract static class Sync extends AbstractQueuedSynchronizer {
  3.         ...
  4.     }
  5.         
  6.     /**
  7.      * Sync object for non-fair locks
  8.      */
  9.     static final class NonfairSync extends Sync {
  10.                 ...
  11.     }
  12.     /**
  13.      * Sync object for fair locks
  14.      */
  15.     static final class FairSync extends Sync {
  16.         ...
  17.     }
  18.     public ReentrantLock() {
  19.         sync = new NonfairSync();
  20.     }
  21.     public ReentrantLock(boolean fair) {
  22.         sync = fair ? new FairSync() : new NonfairSync();
  23.     }
复制代码
5 可重入锁(递归锁)

是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。

  • 可重入锁的种类

    • 隐式:synchronized(自动多次释放)
    • 显示:ReentrantLock(手动多次释放)

6 死锁及排查

死锁是指两个或两个以上的线程在执行过程中,因争夺双方持有的资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足!死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。
  1. jps  # 查看Java进程编号
  2. jstack 进程编号 # 查看死锁情况
复制代码
7 写锁(独占)与读锁(共享)

8 自旋锁SpinLock

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

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


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册