自旋锁
自旋锁是为实现保护共享资源而提出一种锁机制。不会引起资源申请者睡眠,执行单元A目前占用资源,B在等待资源,B一直等待不会进入睡眠,即不会发生上下文切换,可提高性能。
自旋锁应用应当注意过多的耗费cpu资源,和死锁。有以下原则应当遵守
1资源等待者设置尝试次数,超过一定次数放弃锁。
2 递归程序不能在持有自旋锁时调用自己,也不应在递归调用时试图获得相同的自旋锁。
3 自旋锁的持有程序应尽可能短时间的持有锁。如果需要长时间锁定资源,应该用信号量的方式。
自旋锁算法:
do{
b=1;
while(b){//开始b为真,进入循环
lock(bus);//多cput环境,锁总线,保证操作的原子性,再进入临界区,单cpu不必,有单
b = test_and_set(&lock);// 独原子级指令
unlock(bus);
}
临界区
lock = 0;//放开锁
其余部分
}while(1)
java代码实现自旋锁示例:
package test2;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class SpinLock implements Lock {
/**
* 可重入自旋锁
*/
private final AtomicReference<Thread> owner = new AtomicReference<Thread>();
/**
* 锁持有次数,可重入,每次同一个线程每次获得锁,lockHoldNum值加1
*/
private int lockHoldNum;
//获得锁
@Override
public void lock() {
final AtomicReference<Thread> owner = this.owner;//当前锁持有者
final Thread current = Thread.currentThread();//当前线程
if (owner.get() == current) { // 当前线程即当前锁的持有者时,lockHoldNum加1,表示是同一个线程多次获得锁
lockHoldNum++;
return;
}
//尝试获得锁,一直自旋,直到获得锁为止
while (!owner.compareAndSet(null, current)) {
}
lockHoldNum = 1;//将值设置为1,标识已经获得锁
}
//获得锁,与lock方法不同之处在于,等待资源的过程中,如果发生线程中断则抛出异常。
@Override
public void lockInterruptibly() throws InterruptedException {
final AtomicReference<Thread> owner = this.owner;
final Thread current = Thread.currentThread();
if (owner.get() == current) {
++lockHoldNum;
return;
}
while (!owner.compareAndSet(null, current)) {
// 如果当前线程中断,则重置 中断状态,抛出异常
if (current.isInterrupted()) {
current.interrupt();
throw new InterruptedException();
}
}
lockHoldNum = 1;
}
//尝试获取锁,成功返回true
@Override
public boolean tryLock() {
boolean locked = owner.compareAndSet(null, Thread.currentThread());
if (locked) {
lockHoldNum = 1;
}
return locked;
}
//指定时间内获取锁,如果期间等待的线程中断了,抛出异常。
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
final AtomicReference<Thread> owner = this.owner;
final Thread current = Thread.currentThread();
if (owner.get() == current) {
++lockHoldNum;
System.out.println(current + "第" + lockHoldNum +"次获得锁");
return true;
}
final long start = System.nanoTime();
final long timeoutNanos = unit.toNanos(time);
while (!owner.compareAndSet(null, current)) {
// 响应中断
if (current.isInterrupted()) {
current.interrupt();
throw new InterruptedException();
}
// 判断是否超时
long elapsed = System.nanoTime() - start;
if (elapsed >= timeoutNanos) {
return false;
}
}
lockHoldNum = 1;
System.out.println(current + "第" + lockHoldNum +"次获得锁");
return true;
}
//释放锁
@Override
public void unlock() {
final AtomicReference<Thread> owner = this.owner;
final Thread current = Thread.currentThread();
//如果锁的持有者不是当前线程,则异常
if (owner.get() != current) {
throw new IllegalMonitorStateException();
}
// 当前锁定次数为1,直接释放锁,否则减1
if (--lockHoldNum == 0) {
owner.set(null);
}
}
@Override
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
package test2;
import java.util.concurrent.TimeUnit;
public class SpinLockTest {
public static void main(String[] args) throws Exception{
Thread thread1=new Thread(new SpinLockThread());
Thread thread2=new Thread(new SpinLockThread());
Thread thread3=new Thread(new SpinLockThread());
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
System.out.println("main线程结束");
}
}
class SpinLockThread implements Runnable {
static SpinLock spinLock = new SpinLock();
@Override
public void run() {
try {
if(spinLock.tryLock(60,TimeUnit.SECONDS))
try{
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread()+"--->"+i);
}
}finally{
spinLock.unlock();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在run方法中不加锁时,三个线程是交替不确定执行的,加锁之后先获得锁的线程执行完毕后,后一个线程才会执行。