java自旋锁_Java线程 - CAS自旋锁(spin-lock)

本文介绍了自旋锁的概念、原理及其在多处理器系统中的应用,强调了自旋锁适合于锁保持时间短的情况。自旋锁与互斥锁的区别在于,自旋锁不会导致调用者阻塞,而是持续尝试获取锁。另外,文章提到了Java中的CAS(CompareAndSet)原语,这是一种保证操作原子性的机制,常用于并发控制。Java的Atomic包提供了基于CAS的原子类,实现了高效且不阻塞的线程安全。
摘要由CSDN通过智能技术生成

一、自旋锁提出的背景

由于在多处理器系统环境中有些资源因为其有限性,有时需要互斥访问(mutual exclusion),这时会引入锁的机制,只有获取了锁的进程才能获取资源访问。即是每次只能有且只有一个进程能获取锁,才能进入自己的临界区,同一时间不能两个或两个以上进程进入临界区,当退出临界区时释放锁。设计互斥算法时总是会面临一种情况,即没有获得锁的进程怎么办?通常有2种处理方式。一种是没有获得锁的调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,这就是自旋锁,他不用将县城阻塞起来(NON-BLOCKING);另一种是没有获得锁的进程就阻塞(BLOCKING)自己,请求OS调度另一个线程上处理器,这就是互斥锁。

二、自旋锁原理

跟互斥锁一样,一个执行单元要想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。由此我们可以看出,自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题:

递归死锁:试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。

过多占用cpu资源。如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候自旋锁会让其它process动不了. 因此,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会

由此可见,自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。

三、Java CAS

CAS是一种系统原语(所谓原语属于操作系统用语范畴。原语由若干条指令组成的,用于完成一定功能的一个过程。primitive or atomic action 是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性·即原语的执行必须是连续的,在执行过程中不允许被中断)。CAS是Compare And Set的缩写。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性

sun.misc.Unsafe是JDK里面的一个内部类,这个类为JDK严格保护,因为他提供了大量的低级的内存操作和系统功能。如果因为错误的使用了这个类,不会有“异常”被扔出,甚至会造成JVM宕机。这也是为什么这个类的名字是Unsafe的原因。因此当使用这个类的时候,你一定要明白你在干什么。这个类中提供了3个CAS的操作

方法名

解释

compareAndSwapInt(Object object, long address, int expected, int newValue)

比较对象object的某个int型的属性(以地址的方式访问),如果他的数据值是expected,则设定为newValue,返回true;否则返回false

compareAndSwapLong(Object object, long address, long expected, long newValue)

比较对象object的某个long型的属性(以地址的方式访问),如果他的数据值是expected,则设定为newValue,返回true;否则返回false

compareAndSwapLong(Object object, long address, Object expected, Object newValue)

比较对象object的某个Object型的属性(以地址的方式访问),如果他的数据值是expected,则设定为newValue,返回true;否则返回false

四、Java自旋锁应用-原子包

Jdk1.5以后,提供了java.util.concurrent.atomic包,这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。其中的类可以分成4组

AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference

AtomicIntegerArray,AtomicLongArray

AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater

AtomicMarkableReference,AtomicStampedReference,AtomicReferenceArray

我们来看一段AtomicBoolean中的自旋锁的代码

public final boolean getAndSet(booleannewValue) {for(;;) {boolean current =get();if(compareAndSet(current, newValue))returncurrent;

}

}

参考

http://baike.baidu.com/view/1250961.htm?fr=aladdin

STM32F4系列的自旋锁是通过设置特定的寄存器来实现的。在STM32F4中,可以使用内置的DWT(Data Watchpoint and Trace)模块来实现自旋锁。DWT模块可以用来监控程序执行时间,从而实现自旋锁。 在使用自旋锁之前,需要先初始化DWT模块。具体的初始化代码如下: ``` void spin_lock_init(void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; } ``` 在上述代码中,首先将Core Debug模块使能,然后启用DWT模块的计数器。 接下来,可以使用以下代码来实现自旋锁的加锁和解锁操作: ``` void spin_lock(volatile uint32_t* lock) { while (__ldrex(lock) == 1); __strex(1, lock); } void spin_unlock(volatile uint32_t* lock) { __strex(0, lock); } ``` 在上述代码中,spin_lock函数会一直循环检查lock指向的变量是否为1,如果是1,则说明锁已经被其他线程占用,当前线程需要一直等待。如果lock指向的变量是0,则将其设置为1,表示当前线程占用了锁。 spin_unlock函数则是将lock指向的变量设置为0,表示当前线程释放了锁。 需要注意的是,自旋锁是一种非阻塞锁,如果其他线程一直占用锁,当前线程会一直循环等待,这会导致CPU占用率过高。因此,在使用自旋锁时,应该根据具体应用场景来选择合适的锁类型。同时,在STM32F4中,还可以使用信号量和互斥锁等更加高级的锁机制来解决多线程同步问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值