1、Synchronized原理是什么?
Synchronized 是由JVM实现的一种实现互斥同步的一种方式 ,被 Synchronized修饰过的程序块,在编译前后被编译器生成了monitorenter和monitorexit两个字节码指令。
在虚拟机执行到monitorenter指令时, 首先要尝试获取对象的锁:如果这个对象没有锁定, 或者当前线程已经拥有了这对象的锁,把锁的计数器 +1;当执行monitorexit指令时将锁计数器-1;当计数器为0时,锁就被释放了 。如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止 。
Java中Synchronize通过在对象头设置标记,达到了获取锁和释放锁的目的 。
2、对象的锁是什么?如何确定对象的锁?
“锁”的本质其实是monitorenter和monitorexit字节码指令的一个 Reference类型的参数 ,即要锁定和解锁的对象。使用Synchronized 可以修饰不同的对象,因此对应的对象锁可以这么确定 。
①、如果Synchronized明确指 定了锁对象,比如Synchronized(变量名)、Synchronized(this)等 ,说明加解锁对象为该对象。
②、如果没有明确指定:若Synchronized修饰的方法为非静态方法,表示此方法对应的对象为锁对象;若Synchronized修饰的方法为静态方法,则表示此方法对应的类对象为锁对象。注意,当一个对象被锁住时,对象里面所有用Synchronized修饰的方法都将产生堵塞,而对象里非Synchronized修饰的方法可正常被调用,不受锁影响。
3、什么是可重入性,为什么说Synchronized是可重入锁?
可重入性是锁的一个基本要求,是为了解决自己锁死自己的情况。一个类中的同步方法调用另一个同步方法,假如 Synchronized 不支持重入,进入 method2 方法时当前线程获得锁,method2 方法里面执行 method1 时当前线程又要去尝试获取锁,对 Synchronized 来说,可重入性是显而易见的,刚才提到,在执行 monitorenter 指令时,如果这个对象没有锁定,或者当前线程已经拥这时如果不支持重入,它就要等释放,把自己阻塞,导致自己锁死自己。有了这个对象的锁(而不是已拥有了锁则不能继续获取),就把锁的计数器 +1,其实本质上就通过这种方式实现了可重入性。
4、JVM 对Java的原生锁做了哪些优化?
在JDK早期是重量级的;后来改进—>锁升级的概念;不可逆只能升级不能降级,sync的四种状态
markWord记录这个线程的ID(偏向锁);
如果线程争用:升级---->自旋锁(占用cpu)适用于时间段、数量少的线程;
自旋锁触发10次以后升级为重量级锁---->os(不占用cpu);
执行时间短,线程数少用自旋锁;
执行时间长,线程数多用系统锁。
5、为什么说Synchronized是非公平锁?
非公平主要表现在获取锁的行为上,并非是按照申请锁的时间前后给等待线程分配锁的,每当锁被释放后,任何一个线程都有机会竞争到锁,这样做的目的是为了提高执行性能,缺点是可能会产生线程饥饿现象。
6、什么是锁消除和锁粗化?
锁消除:指虚拟机即时编译器在运行时,对一些代码上要求同步,但被检测到不可能存在共享数据竞争的锁进行消除。主要根据逃逸分析。
锁粗化:原则上,同步块的作用范围要尽量小。但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作在循环体内,频繁地进行互斥同步操作也会导致不必要的性能损耗。锁粗化就是增大锁的作用域。
7、为什么说Synchronized是一个悲观锁?乐观锁的实现原理又是什么?什么是CAS,它有什么特性?
Synchronized显然是一个悲观锁,因为它的并发策略是悲观的:不管是否会产生竞争,任何的数据操作都必须要加锁、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要被唤醒等操作。
随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略。先进行操作,如果没有其他线程征用数据,那操作就成功了;如果共享数据有征用,产生了冲突,那就再进行其他的补偿措施。这种乐观的并发策略的许多实现不需要线程挂起,所以被称为非阻塞同步。乐观锁的核心算法是CAS(CompareandSwap,比较并交换),它涉及到三个操作数:内存值、预期值、新值。当且仅当预期值和内存值相等时才将内存值修改为新值。这样处理的逻辑是,首先检查某块内存的值是否跟之前我读取时的一
样,如不一样则表示期间此内存值已经被别的线程更改过,舍弃本次操作,否则说明期间没有其他线程对此内存值操作,可以把新值设置给此块内存。CAS具有原子性,它的原子性由CPU硬件指令实现保证,即使用JNI调用Native方法调用由C++编写的硬件级别指令,JDK中提供了Unsafe类执行这些操作。
8、乐观锁一定就是好的吗?
乐观锁避免了悲观锁独占对象的现象,同时也提高了并发性能。
缺点:
①、.乐观锁只能保证一个共享变量的原子操作。如果多一个或几个变量,乐观锁将变得力不从心,但互斥锁能轻易解决,不管对象数量多少及对象颗粒度大小。
②、长时间自旋可能导致开销大。假如CAS长时间不成功而一直自旋,会给CPU带来很大的开销。
③、ABA问题。CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。