之前我们讲了synchronized的原理,以及其中的偏向锁,送佛送到西,这次我们来说一说轻量级锁吧。
友情提醒:上面两篇没有看过的看官,请先阅读上面两篇之后,再读此篇,这样效果会更佳哦~
开局一张图。
看一张大图
流程讲解
当JVM关闭了偏向锁模式,对象在创建的时候,Mark Word中存储的是hash值,年代,是否偏向锁标志位为0,标志位是01。即一个无锁不可偏向状态。
轻量级锁逻辑:
1.当线程访问同步块,先判断锁状态标志位,如果是00,则说明是轻量级锁,JVM会先在当前线程栈帧中分配Lock Record空间;
2.将锁对象头中的Mark Word拷贝到当前线程的Lock Record中,称为Displaced Mark Word,然后使用CAS,将对象头中的Mark Word修改为指向当前线程栈中Lock Record的指针(如图)。如果成功,则获取轻量级锁,执行同步块中的代码,如果失败,则进行自旋竞争锁,自旋达到一定的次数如果依旧没有获取到锁,则升级为重量级锁(因为自旋会消耗CPU,为了避免无用的自旋,一旦锁升级为重量级锁,就不会恢复到轻量级锁,自旋的线程会被挂起阻塞住);
CAS操作之前堆栈与对象的状态:
CAS操作之后堆栈与对象的状态:
3.执行完同步代码块代码,退出同步代码块,使用CAS开始轻量级锁解锁,解锁的条件需要满足以下两个:
1)对象头Mark Word中锁记录指针是否依旧指向当前线程Lock Record
2)拷贝在当前线程Lock Record的Mark Word信息是否与对象头中的Mark Word一致
4.如果满足,则成功释放锁;
5.如果不满足,则释放锁,唤醒被挂起阻塞的线程,开始重量级锁的竞争。
注:当超过自旋阈值,竞争的线程就会把锁对象Mark Word指向重量级锁,导致Mark Word中的值发生了变化,当原持有轻量级锁的线程执行完毕,尝试通过CAS释放锁时,因为Mark Word已经指向重锁,不再是指向当前线程Lock Record的指针,于是解锁失败,这时原持有轻量级锁的线程就会知道锁已经升级为重量级锁。
偏向锁升级为轻量级锁:
enum data {
success, error } console.log(data.success) // 0 console.log(data.error) // 1 如果指定了值,那么从指定的值往下递增。 enum data { pedding, success = 100, error } console.log(data.pedding) // 0 console.log(data.success) // 100 console.log(data.error) //101 任意类型(any) 在typescript中定义变量时通常需要指定类型,但是有时候我们可能不知道这个变量会是什么类型(比如这个变量是用来接收后段发送过来的数据,这个数据类型可能不太确定。)。这时候我们就可以指定any类型。表示可以是任意类型。 let anyVar:any; anyVar = 123; console.log(anyVar) anyVar = [1,2,3]; console.log(anyVar) 另外,当我们只知道一部分数据的类型时,any类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:我们可以指定数组中元素类型为any。这样的话我们就可以随意地修改数组中的元素了。 let list:any[] = [1,'hello',false]; list[1] = true; // 将数组第二个元素从string类型修改为boolean console.log(list[1]) Null 和 Undefined null和undefined是其他数据类型的子类型。 我们知道在js中定义了变量但是赋值,那么得到的就是undefined。比如: let u ; console.log(u) // undefined 但是在ts中,如果我们定义了一个变量,没有进行赋值。如果我们使用它会出现报错。比如: let u:number; console.log(u) // Variable 'u' is used before being assigned 但是很多情况下,我们定义了一个变量没有进行赋值,在后续我们需要根据这个变量是否有值来进行判断。但是这种情况下就会报错。因此:我们希望能够 在没有获取值的情况下不会报错,这时候就可以使用undefined了。 let u2:number|undefined; console.log(u2) u2 = 123; console.log(u2) 同理Null类型也是为了处理值为null时的情况。 void类型 void类型表示没有任何类型,一般用于定义方法的时候,方法没有返回值。 function warnUser(www.chenhaiyulp.cn): void { console.log("This is my warning message"); } never类型 never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。 let a:never; a = ((www.yunyouuyL.com) => {throw new Error('抛出异常')})() 如果变量有确定的类型,那么使用never类型就会报错。比如: 定义变量n为never类型。但是将n赋值为number类型了,那么就会报错。 let n1:never; n1 = 123; Object类型 object类型表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型。 declare function create(o: object | null):www.baihuidengLu.com void; object类型的值与js中相同。数组,函数,null等都可以是Object类型。 create({ prop: 0 }); // OK create(null); // OK create([1,2,3]); // OK create(function(www.yunyougj.cn){}) // OK create(42); // 报错 create("string"); // 报错 create(false); // 报错1.先在原持有偏向锁的线程栈帧中分配Lock Record;
2.将对象头Mark Word拷贝到原持有偏向锁的线程Lock Record中,然后使用CAS,将对象头中的Mark Word修改为指向当前线程栈中Lock Record的指针。将原持有偏向锁的线程升级为持有偏向锁的线程;
3.唤醒线程,从安全点继续执行,执行完毕解锁。
轻量级锁的重入计数
我们看个demo,在该demo中重复3次获得锁,
synchronized(obj){ synchronized(obj){ synchronized(obj){ } }}
假设锁的状态是轻量级锁,如图反应了Mark Word和线程栈中Lock Record的关系,右边线程栈中包含3个指向当前锁对象的Lock Record。其中栈中最高位的Lock Record为第一次获取锁时分配的,其中Displaced Mark word为锁对象加锁前的Mark Word,而之后的锁重入,则会在线程栈中分配一个Displaced Mark word为null的Lock Record,用来重入计数。
每次释放锁的时候则会删除对应的Lock Record。 这就是轻量级锁的实现逻辑,相对于偏向锁来说,逻辑会稍微简单一些。