问题大纲
- 一、为什么有Synchronized?
- 二、Synchronized是什么?
- 三、Synchronized怎么实现的?
- 四、Synchronized使用?
- 五、Synchronized延申?
- 四、参考
一、为什么有Synchronized?
1、为什么需要 synchronized?什么场景下使用 synchronized?
多个线程操作共享数据时,加锁保证访问共享数据的线程安全性。
二、Synchronized是什么?
1、 synchronized 这三种加锁方式(作用对象:静态方法、非静态方法、代码块)作用范围的区别?(*4)
先明确一点:锁是加在对象上面的,重要事情再说一遍:在对象上加锁(这也是为什么 wait / notify 需要在锁定对象后执行,只有先拿到锁才能释放锁)
这三种作用范围的区别实际是被加锁的对象的区别,请看下表:
作用范围 | 锁对象 |
---|---|
非静态方法 | 当前对象 => this |
静态方法 | 类对象 => SynchronizedSample.class (一切皆对象,这个是类对象) |
代码块 | 指定对象 => lock (以上面的代码为例) |
2、Synchronized修饰的方法在抛出异常时,会释放锁吗?
会,代码执行完毕或者异常结束会释放锁。
试图获取锁的时候不能设定超时,不能中断一个正在使用锁的线程,相对而言,Lock可以中断和设置超时
3、synchronized 是公平锁还是非公平锁?
非公平,新来的线程有可能立即获得监视器,而在等待区中等候已久的线程可能再次等待,不过这种抢占的方式可以预防饥饿。
三、Synchronized怎么实现的?
三.synchronized关键字,是怎么保证线程安全的呢?
内存结构;原子性、 有序性、可见性;锁类型、切换流程、优缺点。
1、synchronized底层源码如何实现?(*4)
synchronized 在代码块上是通过 monitorenter 和 monitorexit 指令实现,在静态方法和 方法上加锁是在方法的 flags 中加入 ACC_SYNCHRONIZED 。
2、synchronized本质上是通过什么保证线程安全的? 分三个方面回答:加锁和释放锁的原理,保证可见性原理。
1、原子性
互斥锁:monitorenter 和 monitorexit 、ACC_SYNCHRONIZED,保证同一时刻只有一个线程执行。
2、有序性
通过Acquire屏障和Release屏障保证代码块内部可以重排,但是代码块内部和代码块外部的指令是不能重排。
3、可见性
进入synchronized代码块时,将用到的变量从该线程的工作内存中清除,转而从主内存中获取。退出synchronized代码块时,会将代码块内用到的变量的修改,刷新到主内存中。
追问1:为什么monitorexit指令出现了两次?
第1次为同步正常退出释放锁;第2次为发生异步退出释放锁。
追问2:可重入原理?
synchronized底层的实现原理是利用计算机系统的mutex Lock实现。每一个可重入锁都会关联一个线程ID和一个锁状态status。
当一个线程请求方法时,会去检查锁状态,如果锁状态是0,代表该锁没有被占用,直接进行CAS操作获取锁,将线程ID替换成自己的线程ID。如果锁状态不是0,代表有线程在访问该方法。此时,如果线程ID是自己的线程ID,如果是可重入锁,会将status自增1,然后获取到该锁,进而执行相应的方法。如果是非重入锁,就会进入阻塞队列等待。
释放锁时,可重入锁,每一次退出方法,就会将status减1,直至status的值为0,最后释放该锁。释放锁时,非可重入锁,线程退出方法,直接就会释放该锁。
所以,从一定程度上来说,可重入锁可以避免死锁的发生。