Java中锁的一些特性

在日常写的代码中,我们常常为了避免线程安全问题,会对部分代码加上锁,比如synchronized锁。锁也有很多的特性,一起来看看锁有哪些特性。

1.乐观锁 和 悲观锁

这是锁的特性之一,乐观锁 和 悲观锁是 一个相对的特性。

在代码编译时,编译器会对代码进行预估,如果预测代码在执行的过程中锁的冲突率并不大,编译器就可以少做一点工作,这种情况下锁就是乐观锁。

反之,如果预测代码在执行过程中锁的冲突率比较大,编译器就要做比较多的工作,这种情况下锁就是悲观锁。

2. 重量级锁 和 轻量级锁

轻量级锁 就是执行代码时,锁的系统开销比较小。与之对应的是 重量级锁,在执行代码时锁的系统开销就比较大。乐观锁通常也就是轻量级锁,悲观锁通常也就是重量级锁,但并不完全绝对的。

3.自旋锁 和 挂起等待锁

轻量级锁是自旋锁的一种典型表现,自旋锁往往是纯在用户态实现的,比如在一个while循环中,不停的检查当前锁有没有被释放,如果没释放,就不断循环检查,若释放了,就可以立刻拿到锁,进而结束循环。这个过程会一直消耗 CPU,但换来了更快的相应速度,系统开销不大。

挂起等待锁就涉及到内核态操作,重量级锁是挂起等待锁的一种典型表现。在代码执行的过程中,一旦涉及到了锁竞争,没拿到锁的线程就得阻塞等待,阻塞等待意味着系统的巨大开销

4. 读写锁

这里把加锁分成了两种,读加锁 写加锁

读加锁,意味着 读的时候只能读,不能写。意思是 线程1在读锁1的时候,线程2也可以来读锁1代码块中的内容,但不能修改其中的内容。

写加锁,意味着 写的时候不能读,也不能写。意思是 线程1在写锁1的时候,线程2不能读锁1代码块中的内容,也不能修改其中的内容。

5. 可重入锁 和 不可重入锁

可重入锁就是一个线程针对同一把锁,加锁两次,不会发生死锁,则这把锁就是可重入锁。

不可重入锁就是一个线程针对同一把锁加锁两次,会发生死锁。

6.公平锁 和 非公平锁

比如有多个线程要去竞争同一把锁时,某一个线程正在使用这把锁准备释放的时候,其他正在等待拿这把锁的线程有很多,到底是按线程“先来后到”的顺序把锁给到某个线程,还是以“均等的概率”给到一个线程呢。按“先来后到”的是公平锁,以“均等概率”是非公平锁

【synchronized特性】

对于以上锁的特性,那么synchronized的特性是以上的哪些呢

对于特性1,2,3,synchronized都是自适应的,并不局限于特定的某一种特性。

什么是自适应呢?比如代码在执行之前,synchronized预测代码中锁的冲突率并不大,就会以乐观锁的模式来执行,也就是轻量级锁,基于自旋锁的方式来实现。但若在实际运行的过程中,发现锁冲突率比较大,synchronized就会升级成悲观锁,也就是重量级锁,基于挂起等待锁的方式来实现。

对于特性4,5,6synchronized不是读写锁,是可重入锁,是非公平锁。操作系统提供的加锁api默认情况下就属于是“非公平锁”,如果要实现“公平锁”,就得引入队列来实现这一特性了。

【synchronized的几个重要机制】

(1)锁升级

synchronized自适应的过程就涉及到锁升级的过程,

锁升级的过程为:无锁-->偏向锁-->轻量级锁-->重量级锁。值得注意的是,锁只能升级,不能降级,是一个单向的过程。下面说明锁升级的过程:

无锁-->重量级锁:代码执行之前,编译器预测某一块加了锁的代码并不需要加锁,编译器会视为无锁,但在实际运行过程中,发现这一块代码可能需要加锁,于是将最开始的无锁变为偏向锁偏向锁并不是真正加锁了,而是一种预加锁状态(比如,有一个重大刑事案件,有嫌疑人,还没明确证据,没将嫌疑人控制起来,时刻关注着这个嫌疑人,准备随时逮捕。)随着代码的执行,发现这一块代码确实需要加锁,但锁的冲突率好像还不太高,于是就将偏向锁升级为轻量级锁。(这个过程意思就是发现部分证据能证明这个嫌疑人与这个案件有关系,于是将他传唤讯问)。接着发现锁的冲突率很高,于是将轻量级锁升级到重量级锁(确定嫌疑人是作案人之一,将其逮捕,并重刑拷打。)从最开始只是观察嫌疑人,不耗费什么大的精力人力,到后来传唤,有专门人员问话,再到逮捕拷打,逐渐耗费更多的人力物力。这整个锁升级过程意味着系统的开销逐渐变大锁的自适应过程也是编译器在性能线程安全之间的不断权衡本着能少一点开销就少一点开销的初心,不到 迫不得已,就不以 基于挂起等待方式 实现的 重量级锁 来 执行代码

(2)锁消除

如果一段代码中有加锁操作,在代码执行之前,编译器无法确定代码是否会有锁冲突,直到代码运行时,若发现代码中加锁部分的代码不需要加锁,编译器就会视这段代码为未加锁,不以锁的机制来执行这段代码,直接把我们加的锁优化掉了。这个行为就是锁消除。编译器只会在非常确定的情况下才会做出 锁消除操作。

(3)锁粗化 

 锁粗化是指锁的粒度逐渐粗化的过程。锁的粒度指的是synchronized代码块中代码的数量,代码越多,锁的粒度越粗,反之代码块中代码越少,锁的粒度越细。但在整体代码中,并不是锁的粒度越细越好,若是以下这种情况,锁更粗的开销反而更小:

 代码1

 代码2

虽然代码1的锁更细,但涉及到反复加锁释放锁的过程,意味着巨大的系统开销,而代码2虽然锁更粗,但只加一次锁,不会反复加锁释放锁,比代码1的开销更小。 

以上关于synchronized的机制的变化都是编译器默默做的工作,我们是感知不到编译器做的这一系列操作的。

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值