Java中synchronized关键字的作用是什么

发布时间:2021-06-30 17:31:27 作者:Leah
来源:亿速云 阅读:276

这篇文章将为大家详细讲解有关Java中synchronized关键字的作用是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

1.  什么是synchronized
我们将其理解为同步锁,可以实现共享资源的同步访问,解决线程并发的安全问题。

1.1 怎么使用的

2.早期的synchronized

JDK1.6之前属于重量级锁,依赖于操作系统的Mutex Lock,Java的线程映射到操作系统的原生线程,需要操作系统申请互斥量,操作系统对线程的切换,需要从用户态切换到内核态,比较耗时,效率底下。

3.对synchronized的优化

JDK1.6之后在JVM层面对synchronized底层做了很多的优化,包括偏向锁,轻量级锁,自旋锁,自适应自旋锁,锁消除,锁粗化等优化技术。

3.1 偏向锁

目的:在没有线程竞争的情况下,减少传统的重量级锁使用操作系统互斥量的开销,提升性能。
特点:  
  1. 在没有锁竞争的情况下,会把整个锁消除
  2. 偏向于第一个获取到偏向锁的线程
  3. 如果在接下来的执行中偏向锁没有被其他线程获取,那么拥有该锁的线程就不需要同步
变化:在锁竞争激烈的场合,偏向锁失效。原因是,在此情况下,极有可能每次申请锁的线程不是同一个线程,所以此时不应该使用偏向锁,否则得不偿失。But,偏向锁失效后,并不会立即膨胀为重量级锁,而是首先升级为轻量级锁。
关于偏向锁的原理可以查看《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版的13章第三节锁优化。

3.2 轻量级锁

当偏向锁失效,JVM不会立即升级为重量级锁,而是试图使用轻量级锁的优化手段(JDK1.6之后加入的)。轻量级锁不是为了替代重量级锁,它的本意是是在没有线程竞争的情况下,减少传统的重量级锁使用操作系统互斥量的开销,提升性能。
目的:和偏向锁一样
特点:  
  1. 和偏向锁不同,轻量级锁使用CAS操作代替重量级锁。
  2. 使用轻量级锁,不需要申请互斥量。
  3. 轻量级锁的加锁和释放锁都是CAS操作。
变化:对于大多数锁来说,在整个同步周期都不存在竞争,这来自经验数据。如果没有竞争,轻量级锁使用CAS操作,避免了使用互斥锁的开销。如果存在竞争,除了互斥锁的开销,还会有额外的CAS操作,所以如果存在锁竞争,轻量级锁比重量级锁更慢。如果竞争激烈轻量级锁会迅速膨胀为重量级锁。
关于轻量级锁的原理可以查看《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版的13章第三节锁优化。

3.3 自旋锁和自适应自旋锁

轻量级锁失效后,JVM避免线程真的在操作系统层面挂起,还会进行一项成为自旋锁的优化手段。
在JDK1.6之前就有这项技术了,只是他是默认关闭的,可以通过参数--XX:+UseSpinning开启。JDK1.6之后默认开启。自旋不能完全替代阻塞,因为它还要占用处理器的时间。如果锁被占用的时间短,那么自旋锁的效果就好;否则,反之。自旋等待的时间必须固定,如果超过限定的次数,仍然没有获取到锁,就挂起线程。自旋默认10次,可以使用参数--XX:PreBlockSpin修改。
3.3.1 为什么会有自旋锁
互斥同步对性能最大的影响是阻塞的实现,因为线程的挂起和恢复都需要转入内核态去完成(用户态到内核态的转换将会耗费一定的时间)。而一般线程持有锁的时间并不会太长,如果仅仅为了这一点时间而挂起或恢复线程将会得不偿失。所以JVM团队就想:"   能否让后面来的请求获取锁的线程等待一会儿而不被挂起?看看持有锁的线程是否很快就会释放锁"。
目的:为了减少线程的挂起和恢复,减少带来的系统开销,引入自旋锁。
3.3.2 如何实现自旋
为了让一个线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术就叫做   自旋
3.3.3 自旋的特点
  1. 执行忙循环
  2. 自旋次数固定(默认10次)
  3. JDK1.6之前默认关闭,之后默认打开
  4. 效果的好坏依赖于锁被占用的时间的长短
3.3.4 自适应自旋锁
另外,在JDK1.6时候引入了自适应自旋锁。改进:自旋次数不是固定的。根据上次同一个锁的自旋次数和锁的拥有者的状态来确定自旋次数。JVM变得越来越聪明了。
与自旋锁的区别就是自旋次数不固定。

3.4 锁消除

即使JVM正在运行,如果检测到共享数据不可能存在竞争,将会执行锁消除操作。这将会节省毫无意义的请求锁的时间。

3.5 锁粗化

原则上我们写代码,总是建议将Synchronized代码块的作用范围限制的尽量小,只在共享数据的实际作用域才进行同步,使需要同步的操作数尽量小,如果存在竞争,等待线程也会尽快拿到锁。
大部分情况下,上面的原则没有问题,但是如果一些列的连续操作都对同一个对象反复加锁解锁,会带来很多不必要的性能消耗。

 

4.Synchronized和ReenTrantLock对比

① 两者都是可重入锁  
两者都是可重入锁。   “可重入锁”概念是:   自己可以再次获取自己的内部锁。   比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。   同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。  
② synchronized 依赖于 JVM 而 ReenTrantLock 依赖于 API  
synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。   ReenTrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。  
③ ReenTrantLock 比 synchronized 增加了一些高级功能  
相比synchronized,ReenTrantLock增加了一些高级功能。   主要来说主要有三点:  
  a.等待可中断;b.可实现公平锁;c.可实现选择性通知(锁可以绑定多个条件)  
如果你想使用上述功能,那么选择ReenTrantLock是一个不错的选择。  
④ 性能已不是选择标准  
在JDK1.6之前,synchronized 的性能是比 ReenTrantLock 差很多。   具体表示为:   synchronized 关键字吞吐量随线程数的增加,下降得非常严重。   而ReenTrantLock 基本保持一个比较稳定的水平。   我觉得这也侧面反映了, synchronized 关键字还有非常大的优化余地。   后续的技术发展也证明了这一点,我们上面也讲了在 JDK1.6 之后 JVM 团队对 synchronized 关键字做了很多优化。   JDK1.6 之后,synchronized 和 ReenTrantLock 的性能基本是持平了。所以网上那些说因为性能才选择 ReenTrantLock 的文章都是错的!JDK1.6之后,性能已经不是选择synchronized和ReenTrantLock的影响因素了!而且虚拟机在未来的性能改进中会更偏向于原生的synchronized,所以还是提倡在synchronized能满足你的需求的情况下,优先考虑使用synchronized关键字来进行同步!优化后的synchronized和ReenTrantLock一样,在很多地方都是用到了CAS操作。  

关于Java中synchronized关键字的作用是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

推荐阅读:
  1. Java中的关键字synchronized 详解
  2. Java并发中Synchronized的作用是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java synchronized

上一篇:SpringCloud gateway怎么修改返回数据

下一篇:mysql版本怎么查询

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》