Java中初始是使用mutex互斥锁,因为互斥锁是会线程等待挂起,而对获取锁后的操作时间比较短暂的应用场景来说,这样的锁会让竞争锁的线程不停的park,unpark 的操作,这样的系统的调用性能是非常糟糕的,为了提高锁的性能,java 在6 默认使用了自旋锁。

在Linux中本身就已经提供了自旋锁的系统调用,在glibc-2.9中就有它的比较简单的实现方法

intpthread_spin_lock (lock) pthread_spinlock_t *lock;

{

asm ("\n"

"1:\t"LOCK_PREFIX"decl %0\n\t"

"jne 2f\n\t"

".subsection 1\n\t"

".align 16\n"

"2:\trep; nop\n\t"

"cmpl $0, %0\n\t"

"jg 1b\n\t"

"jmp 2b\n\t"

".previous"

:"=m"(*lock)

:"m"(*lock));

return0;

}通过总线锁把参数-1保证了减法的原子性,如果减后的值是(0)的代表获得锁,其他线程的线程自旋直到参数变成初始值(1),继续竞争锁,直到获得这把锁。

Java 并没有使用系统自带的自旋锁,自己重写了自旋锁的逻辑,并且增加了自旋的次数的控制。详细见-XX:+UseSpinning 和 -XX:PreBlockSpin=xx

让我们具体来看是如何实现的,注意这是mutex锁中所实现的lock,而并不是synchinized 的锁的spin lock的实现(这个你可以参考synchronizer.cpp里的方法TrySpin_VaryDuration)

intMonitor::TrySpin (Thread *constSelf) {

if(TryLock())return1;

if(!os::is_MP())return0;

intProbes  =0;

intDelay   =0;

intSteps   =0;

intSpinMax = NativeMonitorSpinLimit ;

intflgs    = NativeMonitorFlags ;

for(;;) {

intptr_t v = _LockWord.FullWord;

if((v & _LBIT) ==0) {

if(CASPTR (&_LockWord, v, v|_LBIT) == v) {

return1;

}

continue;

}

if((flgs &8) ==0) {

SpinPause () ;

}

// Periodically increase Delay -- variable Delay form

// conceptually: delay *= 1 + 1/Exponent

++ Probes;

if(Probes > SpinMax)return0;

if((Probes &0x7) ==0) {

Delay = ((Delay <<1)|1) &0x7FF;

// CONSIDER: Delay += 1 + (Delay/4); Delay &= 0x7FF ;

}

if(flgs &2)continue;

// Consider checking _owner's schedctl state, if OFFPROC abort spin.

// If the owner is OFFPROC then it's unlike that the lock will be dropped

// in a timely fashion, which suggests that spinning would not be fruitful

// or profitable.

// Stall for "Delay" time units - iterations in the current implementation.

// Avoid generating coherency traffic while stalled.

// Possible ways to delay:

//   PAUSE, SLEEP, MEMBAR #sync, MEMBAR #halt,

//   wr %g0,%asi, gethrtime, rdstick, rdtick, rdtsc, etc. ...

// Note that on Niagara-class systems we want to minimize STs in the

// spin loop.  N1 and brethren write-around the L1$ over the xbar into the L2$.

// Furthermore, they don't have a W$ like traditional SPARC processors.

// We currently use a Marsaglia Shift-Xor RNG loop.

Steps += Delay ;

if(Self != NULL) {

jint rv = Self->rng[0] ;

for(intk = Delay ; --k >=0; ) {

rv = MarsagliaXORV (rv) ;

if((flgs &4) ==0&& SafepointSynchronize::do_call_back())return0;

}

Self->rng[0] = rv ;

}else{

Stall (Delay) ;

}

}

}

a.os::is_MP() 判断系统是否是多核的系统,在单核下,自旋锁是没有意义的。

b.CASPTR 使用了 Atomic::cmpxchg_ptr 原子语义 cmpxchg 比较替换,如果比较的值相等就替换成需要的值并且返回去比较的值,如果不相同返回被比较的值的内容。

在这里的语义是比较_LockWord.FullWord 和 _Lockword 的值是否相同,如果相同就把_Lockword 的值置换成v|_LBIT(_LBIT的值是1)。

自旋锁的逻辑:判断_LockWord.FullWord bit 0 是否是0,如果是0代表没有占有锁,那就尝试去占有锁,通过原子替换置bit0 为1,如果置换成功那么代表拥有锁,没有则进入自旋。

SpinPause ()  函数

在linux_x86 64位机器上 定义了

.globl SpinPause

.align 16

.type  SpinPause,@function

SpinPause:

rep

nop

movq   $1, %rax

ret

主要在rep, nop 的指令经过编译器后的指令是pause,是用于提高cpu性能的,在官方上描述pase指令是为了避免memory order violation ,有种说法就是cpu是流水线的处理指令的,当原子指令store的时候,而如果有线程同时也在load他的值,那么load 必须等到store 执行成功,这样cpu就无法进行流水线作业了。但我更觉的这是个加强版的nop 也就是多增加几个空的机器周期,一来省电,二来本身spin lock就需要cpu空运行,并且不需要访问内存。

c.SafepointSynchronize::do_call_back()这是一个安全点,提供一个停止自旋锁的切入点,比如vm thread,在做线程dump, 内存 dump的时候,是需要让 自旋锁提前停止的。

d.if (Probes > SpinMax) return 0 ; 当大于自旋的次数的时候,自旋自动退出,也就是前面所说的参数-XX:PreBlockSpin

最后这里还有个比较有意思的方法MarsagliaXORV (rv) ; 是算随机数的,不清楚为什么java让cpu自旋的过程中计算随机数的意义何在,为了不让cpu空转?感觉用spinpase 更合理一点。

随机数在java 自旋锁的运用_Java 中自旋锁的实现相关推荐

  1. ipa去除时间锁_Java中的锁以及sychronized实现机制(十)

    上节讲了线程安全和原子性,其实就是并发代码变成同步,意味着代码只有一个人在使用,这样就不会有问题. (一)Java中的锁 1.自旋锁 为了不放弃CPU执行时间,循环的使用CAS技术对数据尝试进行更新, ...

  2. java 锁定界面_Java中的锁

    java中的锁遵循不同的分类方法,太多了,乐观锁/悲观锁,可重入锁/不可重入锁,有些第一遇到的话,可能还有点懵.刚好周末有时间学习下,总结和梳理下. 一总述 总的来说对java的锁有以下七种分类方法: ...

  3. java中乐观锁_Java中乐观锁与悲观锁的实现

    锁(locking) 业务逻辑的实现过程中,往往需要保证数据访问的排他性.如在金融系统的日终结算 处理中,我们希望针对某个cut-off时间点的数据进行处理,而不希望在结算进行过程中 (可能是几秒种, ...

  4. java i 线程不安全_java中的++i是线程安全的吗?

    java中的++i是线程安全的吗?为什么?怎么使它线程安全呢? 先说答案: 非线程安全 先说下为什么是非线程安全的? 从Java内存模型说起 Java内存模型规定了所有的便利都存储在主内存中,每个线程 ...

  5. java什么时候需要同步_JAVA中线程在什么时候需要同步和互斥

    JAVA中线程在什么时候需要同步和互斥 关注:265  答案:6  mip版 解决时间 2021-01-27 08:10 提问者时光易老 2021-01-27 03:32 JAVA中线程在什么时候需要 ...

  6. java i线程安全吗_Java中 i++ 是线程安全的么?为什么?

    问题 在 int i = 0; i = i++; 语句中,i = i++是线程安全的么?如果不安全,请说明上面操作在JVM中的执行过程,为什么不安全?说出JDK中哪个类能达到以上的效果,并且是线程安全 ...

  7. java boolean几个字节_Java中boolean类型到底占用多少个字节?

    1.时间:2017-07-03 07:37:06YuanMxy 2.问题描述:今天在复习java基础的时候发现一小问题,Java中boolean类型到底占用多少个字节? 3.问题解答: (1)什么是b ...

  8. java 基本类型的引用_Java中的基本数据类型与引用数据类型

    一.基本数据类型 byte.short.int.long(整数类型) float.double(浮点数类型) char(字符型) boolean(布尔类型 ) Java数据大多数存放在堆栈中.栈区:存 ...

  9. java常见的报错_Java中常见的错误有哪些?

    原标题:Java中常见的错误有哪些? 1.java.lang.Error 错误.是所有错误的基类,用于标识严重的程序运行问题.这些问题通常描述一些不应被应用程序捕获的反常情况. 原因: 1.对系统所访 ...

最新文章

  1. 视频直播技术详解(3)编码和封装
  2. 打包android阴影不见,Android无pading超简单超实用阴影解决方案
  3. 未來用工新趨勢_数字化商业浪潮来袭 未来用工新趋势成焦点
  4. Linux下C++ UDP Socket例子
  5. python操作redis实例_Java,php,Python连接并操作redis实例
  6. deepin中自定义安装的软件如何加入到启动器
  7. oracle分析函数-排名函数
  8. php fork demo,php多进程demo
  9. Linux 如何配置 SFTP 来代替单一ftp应用
  10. 手机上将png转pdf_如何在Windows 10上将Android智能手机用作网络摄像头
  11. python数据分析18-21
  12. php bi报表,PowerBI开发 报表设计技巧
  13. app内录屏开发 ios_iOS端屏幕录制Replaykit项目实践
  14. 水溶性/脂溶性/Cy3/Cy3.5/Cy5 NHS ester 染料在活体成像中的应用
  15. 幸福婚庆策划网管理系统
  16. macbook视频格式转换_告别格式工厂的视频格式转换方法(mac版 命令行)
  17. fasterRCNN系列
  18. k8s中配置namespace的最小和最大内存
  19. SQL语句执行顺序详解
  20. Hive集成Phoenix

热门文章

  1. 那些不得不说的性能优化套路
  2. 移动护理、护士工作站 UI界面及业务
  3. java多线程模拟购买火车票
  4. python三菱fx3u通讯mx_LabVIEW与三菱FX3U PLC通讯问题总结
  5. 网站算法石榴算法严厉打击下 网站盈利方向何在
  6. 从OA到COP,致远互联成引领行业的“灯塔”
  7. UIPath 如何判断对象为 null
  8. 计算机培训方案范文,2015年软件培训方案模板
  9. html抢答器代码,基于FPGA的四路抢答器的Verilog HDL代码.doc
  10. 计算机word操作试题,计算机一级Word操作题及答案