1、背景java
在看并发包源码的时候看见过LockSupport,今天恰巧看到LockSupport字眼,因而看下jdk1.7中的源码结构。想着它应该是运用多线程的锁工具的,到底彷佛怎么实现的呢?
多线程
2、使用并发
因而本身写个demo对比下synchronized框架
场景:main线程中建立一个线程A,想让threadA 循环完毕的时候先阻塞,而后再main线程中释放。ide
1.控制多线程并发:工具
1 public static void main(String[] args) { 2 Object obj = new Object(); 3 generalSync(obj); 4 obj.notifyAll(); 5 System.out.println("主线程执行完毕"); 6 } 7 8 public static void generalSync(final Object obj) { 9 Runnable runnable = new Runnable() { 10 @Override 11 public void run() { 12 for (int i = 0; i < 10000; i++) { 13 System.out.println(i); 14 } 15 System.out.println("循环完毕"); 16 try { 17 obj.wait(); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 System.out.println("线程A执行完毕"); 22 } 23 }; 24 Thread threadA = new Thread(runnable); 25 threadA.start(); 26 }
上面这个最终结果是什么呢情?会以异常结束:java.lang.IllegalMonitorStateException, 产生的缘由是Object的wait,notify,notifyAll方法必须在同步块中执行spa
2.使用synchronized但主线程接触阻塞在threadA阻塞执行以前线程
1 public static void main(String[] args) { 2 Object obj = new Object(); 3 generalSync(obj);
// Thread.sleep(1000); 4 synchronized (obj) { 5 obj.notifyAll(); 6 } 7 System.out.println("主线程执行完毕"); 8 } 9 10 public static void generalSync(final Object obj) { 11 Runnable runnable = new Runnable() { 12 @Override 13 public void run() { 14 for (int i = 0; i < 10000; i++) { 15 System.out.println(i); 16 } 17 System.out.println("循环完毕"); 18 try { 19 synchronized (obj) { 20 obj.wait(); 21 } 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 System.out.println("线程A执行完毕"); 26 } 27 }; 28 Thread threadA = new Thread(runnable); 29 threadA.start(); 30 } 31
上面会致使一直阻塞,由于主线程在第3行开启一个threadA后,就往下执行4~7代码,而threadA须要循环所用的时间更久。所以会先调用obj.notifyAll(),而后再obj.wait()致使调用顺序错误一直阻塞。想要达到咱们预期的目的,根据threadA时间,能够在第4行添加Thread.sleep(1000)使主线程中的obj.notifyAll()晚于obj.wait()执行。code
3.使用LockSupport来实现同步对象
1 public static void main(String[] args) { 2 Thread threadA = generalSync(); 3 LockSupport.unpark(threadA); 4 System.out.println("主线程执行完毕"); 5 } 6 7 public static Thread generalSync() { 8 Runnable runnable = new Runnable() { 9 @Override 10 public void run() { 11 for (int i = 0; i < 10000; i++) { 12 System.out.println(i); 13 } 14 System.out.println("循环完毕"); 15 LockSupport.park(); 16 System.out.println("线程A执行完毕"); 17 } 18 }; 19 Thread threadA = new Thread(runnable); 20 threadA.start(); 21 return threadA; 22 }
经过LockSupport无需关于阻塞和释放的调用前后问题,仅仅经过park/unpark便可阻塞或释放。park/unpark模型真正解耦了线程之间的同步,线程之间再也不须要一个Object或者其它变量来存储状态,再也不须要关心对方的状态。
3、阅读源码
经过类注释介绍,LockSupport是JDK中比较底层的类,用来建立锁和其余同步工具类的基本线程阻塞原语。java锁和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是经过调用 LockSupport .park()和 LockSupport .unpark()实现线程的阻塞和唤醒 的,下面重点关注着2个方法。
1 public class LockSupport { 2 private LockSupport() {} // Cannot be instantiated. 3 4 // Hotspot implementation via intrinsics API 5 private static final Unsafe unsafe = Unsafe.getUnsafe(); 6 private static final long parkBlockerOffset;
1.根据上面的类定义,内部构造方法私有化,外部类没法实例,做为工具类使用的意图。类中实例化了一个Unsafe这个可直接操做内存的本地API
1 public static void park(Object blocker) { 2 Thread t = Thread.currentThread(); 3 setBlocker(t, blocker); 4 unsafe.park(false, 0L); 5 setBlocker(t, null); 6 }
2.阻塞是经过park方法,第2行获取当前线程;第3行执行setBlocker(Thread t, Object arg),setBlocker从名字感受设置阻塞的地方;第4行调用Unsafe中public native void park(boolean paramBoolean, long paramLong)产生阻塞;第5行调用setBlocker(Thread t, Object arg),可是Object参数是null,此处应该是去除阻塞点。
1 public class LockSupport { 2 private LockSupport() {} // Cannot be instantiated. 3 4 // Hotspot implementation via intrinsics API 5 private static final Unsafe unsafe = Unsafe.getUnsafe(); 6 private static final long parkBlockerOffset; 7 8 static { 9 try { 10 parkBlockerOffset = unsafe.objectFieldOffset 11 (java.lang.Thread.class.getDeclaredField("parkBlocker")); 12 } catch (Exception ex) { throw new Error(ex); } 13 } 14 15 private static void setBlocker(Thread t, Object arg) { 16 // Even though volatile, hotspot doesn't need a write barrier here. 17 unsafe.putObject(t, parkBlockerOffset, arg); 18 }
3.setBlocker是调用Unsafe中public native void putObject(Object paramObject1, long paramLong, Object paramObject2)方法,这个long类型paramLong传的是Thread在内存中的偏移量。经过8~13能够看出在静态代码块中经过Unsafe中public native long objectFieldOffset(Field paramField)来获得内存中位置(以前原子操做类也是经过此方法来获取偏移量)这个偏移量属性名称是"parkBlocker",类型从第11行能够知道parkBlocker是java.lang.Thread类中的一个属性,
public class Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does. */ private static native void registerNatives(); static { registerNatives(); } ... ... /** * The argument supplied to the current call to * java.util.concurrent.locks.LockSupport.park. * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker * Accessed using java.util.concurrent.locks.LockSupport.getBlocker */ volatile Object parkBlocker;
这个parkBlocker应该是Thread类变量。首先LockSupport 是构造方法私有化,相似一个工具类,并且parkBlockerOffset是static类型的,因此即便在多个场景下的多线程环境,不一样的多个Thread调用setBlocker方法都只是针对Thread的类变量进行赋值(类变量只有一个)因此是多对一的关系。而且经过getBlocker源码注释能够看出
1 /** 2 * Returns the blocker object supplied to the most recent 3 * invocation of a park method that has not yet unblocked, or null 4 * if not blocked. The value returned is just a momentary 5 * snapshot -- the thread may have since unblocked or blocked on a 6 * different blocker object. 7 * 8 * @param t the thread 9 * @return the blocker 10 * @throws NullPointerException if argument is null 11 * @since 1.6 12 */ 13 public static Object getBlocker(Thread t) { 14 if (t == null) 15 throw new NullPointerException(); 16 return unsafe.getObjectVolatile(t, parkBlockerOffset); 17 }
从注释看出“返回的是最近被阻塞的对象,相似一个瞬间的快照”,那么我理解Thread中Object parkBlocker属性可能会被多个线程赋值。这个属性跟并发阻塞并没有关系,只是起到记录阻塞对象的做用。
至于Unsafe类中native方法,就没有去追究。看其余博客理解到的是:在hotspot里每一个java线程都有一个Parker实例,Parker里使用了一个无锁的队列在分配释放Parker实例。Parker里面有个变量,volatile int _counter 至关于许可,二元信号量(相似0和1)
当调用park时,先尝试直接可否直接拿到“许可”(即_counter>0时)若是成功,则把_counter设置为0,并返回;若是不成功,则构造一个ThreadBlockInVM,而后检查_counter是否是>0,若是是,则把_counter设置为0
当unpark时,则简单多了,直接设置_counter为1,若是_counter以前的值是0,则还要调用唤醒在park中等待的线程: