目录
16.sychronized修饰方法在抛出异常时,会释放锁吗?
17.多个线程等待同一个sychronized锁的时候,JVM如何选择下一个获取锁的线程?
18.sychronized是公平锁吗?
19.volatile关键字的作用是什么?
20.volatile能保证原子性吗?
21.volatile是如何实现可见性的?
22.volatile是如何实现有序性的?
23.说下volatile的应用场景?
24.所有的final修饰的字段都是编译期常量吗?
25.如何理解private所修饰的方法是隐式地final?
16.sychronized修饰方法在抛出异常时,会释放锁吗?
会
17.多个线程等待同一个sychronized锁的时候,JVM如何选择下一个获取锁的线程?
非公平锁,即抢占式。
18.sychronized是公平锁吗?
synchronized实际上是非公平的,新来的线程有可能立即获得监视器,而在等待区中等候已久的线程可能再次等待,这样有利于提高性能,但是也可能导致饥饿现象。
19.volatile关键字的作用是什么?
防重排序
我们从一个最经典的例子来分析重排序问题。大家应该都很熟悉单例模式的实现,而在并发环境下的单例实现方式,我们通常可以采用双重检查加锁(DCL)的方式来实现。
其源码如下:
现在我们分析以下为什么要在变量singleton之间加上volatile关键字。要理解这个问题,先要了解对象的构造过程,实例化一个对象其实可以分三个步骤:
1.分配内存空间。
2.初始化对象。
3.将内存空间的地址赋值给对应的引用。
但是由于操作系统可以对指令进行重排序,所以上面的过程也可能会变成如下过程:
1.分配内存空间。
2.将内存空间的地址赋值给对应的引用。
3,初始化对象
如果是这个流程,多线程环境下就可能将一个未初始化的对象引用暴露出来,从而导致不可预料的结果。因此,为了防止这个过程的重排序,我们需要将变量设置为volatile类型的变量。
实现可见性
可见性问题主要指一个线程修改了共享变量值,而另一个线程却看不到。引起可见性问题的主要原因是每个线程拥有自己的一个高速缓冲区-线程工作内存。
volatile关键字能有效的解决这个问题,我们看下下面的例子,就可以直到其作用:
直观上说,这段代码的结果只可能有两种:b=3;a=3或者b=2;a=1。不过运行上面的代码(可能时间上要长一些),你会发现除了上面两种结果之外,还出现了另外两种结果:
为什么会出现b=2;a=3和b=3;a=1这种结果呢?正常情况下,如果先执行change方法,再执行print方法,输出结果应该为b=3;a=3。相反,如果先执行的print方法,再执行change方法,结果应该是b=2;a=1。那b=3;a=1的结果是怎么出来的呢?原因就是第一个线程将值a=3修改后,但是对第二个线程是不可见的,所以才出现这一结果。如果将a和b都改成volatile类型的变量再执行,则再也不会出现b=2;a=3;a=1的结果了。
保证原子性:单次读/写
volatile不能保证完全的原子性,只能保证单次读/写操作具有原子性。
20.volatile能保证原子性吗?
不能完全保证,只能保证单次的读/写操作具有原子性。
21.volatile是如何实现可见性的?
内存屏障。
22.volatile是如何实现有序性的?
happens-before等
23.说下volatile的应用场景?
使用volatile必须具备的条件:
1.对变量的写操作不依赖于当前值
2.该变量没有包含在具体其他变量的不变式中。
3.只有在状态真正独立于程序内其他内容时才能使用volatile。
例子1:单例模式
单例模式的一种实现方式,但很多人会忽略volatile关键字,因为没有该关键字,程序也可以很好的运行,只不过代码的稳定性综合不是100%,说不定在未来的某个时刻,隐藏的Bug就出来了。
例子2:volatile bean
在volatile bean模式中,JavaBean的所有数据成员都是volatile类型的,并且getter和setter方法必须非常普通-除了获取或者设置相应的属性外,不能包含任何逻辑。此外,对于对象引用的数据成员,引用的对象必须是有效不可变的。(这将禁止具有数组的属性,因为当数组引用被声明为volatile时,只有引用而不是数组本身具有volatile语义)。对于任何volatile变量,不变式或约束都不能包含JavaBean属性。
24.所有的final修饰的字段都是编译期常量吗?
不是
25.如何理解private所修饰的方法是隐式地final?
类中所有private方法都隐式地指定为final的,由于无法取用private方法,所以也就不能覆盖他。可以对private方法增添final关键字,但这样做并没有什么好处。
看下下面的例子:
Base和Son都有方法test(),但是这并不是一种覆盖,因为private所修饰的方法是隐式地final,也及时无法被继承,所以更不用说是覆盖了,在Son中test()方法不过是属于Son的新成员罢了,Son进行向上转型得到father,但是father.test()是不可执行的,因为Base中的test()方法是private的,无法被访问到。