1:synchronnized概述
synchronized修饰的方法或代码块相当于并发中的临界区,即在同一时刻jvm只允许一个线程进入执行。synchronized是通过锁机制实现同一时刻只允许一个线程来访问共享资源的。另外synchronized锁机制还可以保证线程并发运行的原子性,有序性,可见性。
2:synchronized的实现原理
Monitor被翻译为监视器或管程
每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级)之后,该对象头的Mark Word中就被设置指向Monitor对象的指针
刚开始Monitor中Owner为null当Thread-2执行synchronized(obj)就会将Monitor的所有者Owner置为Thread-2,Monitor中只能有一个Owner
在Thread-2上锁的过程中,如果Thread-3,Thread-4,Thread-5也来执行synchronized(obj),就会进入EntryList BLOCKED
Thread-2执行完同步代码块的内容,然后唤醒EntryList中等待的线程来竞争锁,竞争的时是非公平的
注意
synchronized必须是进入同一个对象的monitor才有上述的效果
不加synchronized的对象不会关联监视器,不遵从以上规则
3:为什么要对synchronized进行优化
Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。因此,这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”。
在jdk1.5之前,只有synchronized重量级锁,实现需要借助操作系统,是比较消耗性能的操作,在1.6之中为了提高性能,便对synchronized锁进行了优化,实现了各种锁优化技术,如:适应性自旋,锁消除,锁粗化,轻量级锁,偏向锁。
4:轻量级锁
(1):是什么
轻量级锁是jdk1.6中加入的新型锁机制,它名字中的“轻量级”是相对于使用操作系统互斥量来实现的重量级锁而言的。首先需要强调的是,轻量级锁并不是来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统重量级锁使用操作系统互斥量产生的性能消耗。在解释轻量级锁的执行过程之前,先明白一点,轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。
(2):上锁的过程
- 在轻量级锁的执行过程上,在代码进入同步块的时候,如果此同步对象没有被锁定,也就是说此时对象的锁标记位为“01”状态,那么虚拟机首先将在线程的栈帧中建立一个名为锁记录(Lock Record)的空间,内部可以储存锁定对象的MarkWord。
-
然后虚拟机将使用CAS操作尝试将栈帧中的Lock Record更新为指向该对象的Mark Word的指针,如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位将转变位00,即表示此对象处于轻量级锁定状态,
-
如果cas操作更新失败的话
-
对比重量级别的锁:
如果线程之间不存在锁的竞争,与重量级锁相比,轻量级锁避免使用了互斥信号量,只使用了简单的CAS操作,但如果存在锁竞争,轻量级锁除了使用互斥信号量,还要额外发生CAS操作,因此在有竞争的情况下,轻量级锁会比重量级锁开销更大。
5:锁膨胀
(1):是什么
如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。
(2):如何形成的
那么当Thread-0去使用CAS去解锁的时候,就不能按原先的步骤进行了,因为现在的锁为重量级锁
6:自旋锁
自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。但是线程自旋是需要消耗cpu的,说白了就是让cpu在做无用功,如果一直获取不到锁,那线程也不能一直占用cpu自旋做无用功,所以需要设定一个自旋等待的最大时间。
如果持有锁的线程执行的时间超过自旋等待的最大时间仍没有释放锁,这时其他争用线程会停止自旋进入阻塞状态。
7:偏向锁
(1):是什么
Java偏向锁是在jdk1.6中引入的,它的目的是消除数据无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除连CAS都不做。
偏向锁,顾名思义,它会偏向于第一个访问它的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用锁的情况,则持有偏向锁的线程将永远是不需要在进行同步。
如果运行过程中,遇到其它线程抢占资源,则持有偏向锁的线程会被挂起,jvm会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
(2):为什么不直接使用轻量级锁
引入偏向级锁是为了减少在无多线程竞争的情况下,尽量减少不必要的轻量级锁执行路径。
因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换线程时,使用CAS操作把获取到这个锁的线程线程的ID记录在对象的Mark Word之中,从而减少性能消耗,不过遇到多线程竞争的情况时就必须撤销偏向锁。
另外一个原因就是,在虚拟机中,大多时候是不存在锁竞争的,常常是一个线程多次获取同一个锁,因此直接使用轻量级锁会增加很多不必要的消耗,所以可以才引入了偏向锁。
(3):偏向锁的使用背景
偏向锁可以提高带有同步但无竞争的程序性能。但是如果程序中大多数锁总是被多个不同的线程访问,那么偏向锁模式就是多余的。