LockSupport源码分析(JDK 1.7)

LockSupport是用来创建锁和其他同步类的基本线程阻塞基本体(primitives)。通过调用LockSupport的park方法可以申请一个许可,如果当前许可可用的话,那么则立即返回,否则则阻塞等待许可。直到另外一个线程调用unpark方法对被阻塞的线程的许可进行释放。(默认许可阻塞)

park方法还支持Blocker对象参数,在调用park方法对当前线程进行阻塞时候,可以把Blocker对象参数传递过来,LockSupport的park方法里会调用setBlocker方法把该对象参数存入到Thread里的parkBlocker变量里。我们可以通过getBlocker获取线程的parkBlocker变量,从而得知该线程正在被阻塞的原因。

public class LockSupport {
    private LockSupport() {} // 禁止创建对象

    // Unsafe实际上调用的都是本地方法native
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long parkBlockerOffset;

    static {
        try {
        //获取Thread的成员变量parkBlocker的偏移量
            parkBlockerOffset = unsafe.objectFieldOffset
                (java.lang.Thread.class.getDeclaredField("parkBlocker"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    //设置Blocker对象到Thread对象里面
    private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn't need a write barrier here.
        unsafe.putObject(t, parkBlockerOffset, arg);
    }

    //获取先前park时候存入的线程的Blocker,如果未存入或者当前线程并不在park里,则为null
    public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return unsafe.getObjectVolatile(t, parkBlockerOffset);
    }




    //对于给定的Thread释放它所占有的许可。如果当前线程已经占有许可,则释放。如果为占有也释放,那么下次调用该线程的unpark方法时候
    //会导致park直接获取了。因为上次已经释放了
    public static void unpark(Thread thread) {
        if (thread != null)
            unsafe.unpark(thread);
    }

    //挂起当前线程,且把Blocker存到线程中
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
    //false代表非绝对时间(即相对时间)
        unsafe.park(false, 0L);
        setBlocker(t, null);
    }
}

以上是LockSupport源码的分析,除了上面说提到的Park(Object obj),还有两个Park重载方法,分别提供了可设定线程挂起的绝对时间和相对时间方法。

下面是个LockSupport的使用例子
利用LockSupport实现了一个先进先出(FIFO)阻塞线程队列功能,即每一条线程入队后都只能排队依次等待执行

 class FIFOMutex {
   private final AtomicBoolean locked = new AtomicBoolean(false);
   private final Queue waiters = new ConcurrentLinkedQueue();

   public void lock() {
     boolean wasInterrupted = false;
     //首先获取当前线程,然后存入waiters 队列头部
     Thread current = Thread.currentThread();
     waiters.add(current);

     // 先循环判断是否满足线程挂起条件
     while (waiters.peek() != current ||
            !locked.compareAndSet(false, true)) {
        //把当前的FIFOMutex 对象传递到 LockSupport.park(this),LockSupport会把this给赋值到当前Thread的变量里
        LockSupport.park(this);
        if (Thread.interrupted()) // ignore interrupts while waiting
          wasInterrupted = true;
     }
     //上面那段while循环主要是负责阻塞挂起线程。当挂起条件不满足后则跳出循环
     //删除队列头部已被阻塞完成的线程
     waiters.remove();
     if (wasInterrupted)          // reassert interrupt status on exit
        current.interrupt();
   }
   //释放线程
   public void unlock() {
     locked.set(false);
     LockSupport.unpark(waiters.peek());
   }
 }}

//TestFIFOMutex是FIFOMutex的测试例子
import java.util.Date;


public class TestFIFOMutex {

    private static FIFOMutex mutex = new FIFOMutex();

    public static void main(String[] args) {
        new Thread(new Runnable(){
            @Override
            public void run() {
                sleep(1000 * 2);
                System.out.println(new Date().toGMTString()+" :t1 准备lock");
                mutex.lock();
                System.out.println(new Date().toGMTString()+" :t1 完成lock");
            }
        }).start();

        new Thread(new Runnable(){
            @Override
            public void run() {
                sleep(1000 * 3);
                System.out.println(new Date().toGMTString()+" :t2 准备lock");
                mutex.lock();
                System.out.println(new Date().toGMTString()+" :t2 完成lock");
            }
        }).start();


        new Thread(new Runnable(){
            @Override
            public void run() {
                sleep(1000 * 5);
                System.out.println(new Date().toGMTString()+" :t3 准备 unlock释放");
                mutex.unlock();
                System.out.println(new Date().toGMTString()+" :t3 释放完毕unlock");
            }
        }).start();
    }

    public static void sleep(long num){
        try {
            Thread.sleep(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

//TestFIFOMutex执行结果
24 Nov 2015 14:33:46 GMT :t1 准备lock
24 Nov 2015 14:33:46 GMT :t1 完成lock
24 Nov 2015 14:33:47 GMT :t2 准备lock
24 Nov 2015 14:33:49 GMT :t3 准备 unlock释放
24 Nov 2015 14:33:49 GMT :t2 完成lock
24 Nov 2015 14:33:49 GMT :t3 释放完毕unlock

通过上面这个测试,可以观察到t2 完成lock必须得等到t3进行unlock。因为t2线程已经给park挂起了,必须得等待unpark唤醒

你可能感兴趣的:(juc,AQS,LockSuppor)