首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

自旋对于synchronized关键字的底层意义

JVM中的同步是基于进入与退出监视器对象Monitor(管程对象)来实现的, 每个对象实例都会有一个Monitor对象, Monitor对象会和Java对象一同创建并销毁。Monitor对象是由c++来实现的。当多个线程同时访问一段同步代码时, 这些线程会被放到一个Entry List集合中, 处于阻塞状态的线程都会被放到该列表当中。接下来, 当线程获取到对象的Monitor时, Monitor是依赖于底层操作系统的mutex lock来实现互斥的, 线程获取mutex成功, 则会持有该mutex, 这时其他线程就无法再获取到该mutex。

如果线程调用了wait方法, 那么该线程就会释放掉所持有的mutex, 并且该线程会进入到wait Set集合(等待集合) 中, 等待下一次被调用notify/notifyAll唤醒。如果当前线程顺利执行完毕方法, 那么它也会释放掉所持有的mutex。同步锁在这种实现方式当中, 因为Monitor是依赖于底层的操作系统实现, 这样就存在用户态与内核态之间的切换, 所以会增加性能开销。

通过对象互斥锁的概念来保证共享数据操作的完整性。每个对象都对应于一个可称为『互斥锁』的标记,这个标记用于保证在任何时刻,只能有线程访问该对象。

那些处于EntryList与Wait Set中的线程均处于阻塞状态, 阻塞操作是由操作系统来完成的, 在linux下是通过pthread_mutex_lock函数实现的。线程被阻塞后便会进入到内核调度状态,这会导致系统在用户态与内核态之间来回切换,严重影响锁的性能。

解决上述问题的办法便是自旋。其原理是:当发生对Monitor的争用时, 若Owner能够在很短的时间内释放掉锁, 则那些正在争用的线程就可以稍微等待一下(即所谓的自旋) , 在Ownr线程释放锁之后,争用线程可能会立刻获取到锁,从而避免了系统阻塞。不过当Owner运行的时间超过临界值后,争用线程自旋一段时间后依然无法获取到锁,这时争用线程则会停止自旋而进入到阻塞状态。所以总体的思想是:先自旋,不成功再进行阻塞,尽量降低阻塞的可能性,这对那些执行时间很短的代码块来说有极大的性能提升。显然,自旋在多处理器(多核心)上有意义。

自旋锁在JDK1.4.2中已经引入,只不过默认是关闭的,可以使用-XX:UseSpinning参数来开启,在JDK1.6中已经默认开启,自旋次数的默认值是10次,超过10次的话,线程会挂起。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200711A05PNF00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券