自旋锁

自旋锁是为实现保护共享资源而提出一种锁机制。不会引起资源申请者睡眠,执行单元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方法中不加锁时,三个线程是交替不确定执行的,加锁之后先获得锁的线程执行完毕后,后一个线程才会执行。

 

posted @ 2019-11-11 16:50  纵马天涯  阅读(294)  评论(0编辑  收藏  举报