LockSupport源码分析(JDK 1.7)
2015-11-24 17:59
441 查看
LockSupport是用来创建锁和其他同步类的基本线程阻塞基本体(primitives)。通过调用LockSupport的park方法可以申请一个许可,如果当前许可可用的话,那么则立即返回,否则则阻塞等待许可。直到另外一个线程调用unpark方法对被阻塞的线程的许可进行释放。(默认许可阻塞)
park方法还支持Blocker对象参数,在调用park方法对当前线程进行阻塞时候,可以把Blocker对象参数传递过来,LockSupport的park方法里会调用setBlocker方法把该对象参数存入到Thread里的parkBlocker变量里。我们可以通过getBlocker获取线程的parkBlocker变量,从而得知该线程正在被阻塞的原因。
以上是LockSupport源码的分析,除了上面说提到的Park(Object obj),还有两个Park重载方法,分别提供了可设定线程挂起的绝对时间和相对时间方法。
下面是个LockSupport的使用例子
利用LockSupport实现了一个先进先出(FIFO)阻塞线程队列功能,即每一条线程入队后都只能排队依次等待执行
通过上面这个测试,可以观察到t2 完成lock必须得等到t3进行unlock。因为t2线程已经给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唤醒
相关文章推荐
- Java并发编程:阻塞队列
- Java多线程(七)之同步器基础:AQS框架深入分析
- 读LockSupport源码
- 读AbstractQueuedSynchronizer类源码
- JUC 基础内容概述
- J.U.C包介绍
- JDK并发工具类源码学习系列——CopyOnWriteArrayList
- 初步理解AQS
- ConcurrentHashMap源码分析
- JUC AbstractQueuedSynchronizer原理解析
- ConcurrentSkipListMap原码解析
- ConcurrentLinkedQueue原码解析
- Java多线程之内存可见性
- current包下CyclicBarrier的使用
- current包中的CountDownLatch的使用
- AQS实现原理及成果(有图有真相)
- 扒一扒ReentrantLock以及AQS实现原理
- Java 并发 (AQS)
- AbstractQueuedSynchronizer 源码分析(共享锁)
- AbstractQueuedSynchronizer整体解析