LockSupport理解

小说:女性最佳生育年龄延后 30-39岁生的娃更聪明?作者:北宗更新时间:2019-03-25字数:76440

他很清楚,丁宁现在人气颇高,之所以不愿意马上签约,想征询家人意见的可能性,当然有。

发现儿童心理健康问题的11个信号

不过刘皓越是如此阿波罗的杀心也就越发的浓郁,如此优秀的存在绝对不能让他再逍遥自在下去不然下一次要找到他就难了。
但在此刻此地,他柳如叶最大,手下的一看上司不让开火,当然只能停止了射击,只见那两个鬼子一看柳如叶对他们伸出来小手指,便气急败坏的“哇哇”乱叫着冲上来,两把亮闪闪的东洋刀一左一右朝柳如叶的脑门子劈下来,那势头简直是要把柳如叶生生的劈成两半啊。

田博光哈哈大笑着,跟王小民熊抱了一下,说道:“小民,我就说咱们兄弟缘分不浅嘛,没想到又见面了。对了,你之前不是已经离开港岛了吗?”

LockSupport理解


一、背景

在看并发包源码的时候看见过LockSupport,今天恰巧看到LockSupport字眼,于是看下jdk1.7中的源码结构。想着它应该是运用多线程的锁工具的,到底似乎怎么实现的呢?

二、使用

于是自己写个demo对比下synchronized

场景:main线程中创建一个线程A,想让threadA 循环完毕的时候先阻塞,然后再main线程中释放。

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方法必须在同步块中执行

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()执行。

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或者其它变量来存储状态,不再需要关心对方的状态

三、阅读源码

通过类注释介绍,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中等待的线程:

当前文章:http://zxqss.com/content/201811/23/content_45645.html

发布时间:2019-03-25 03:07:18

牛仔裤不会一直等你 男人和女人的六个思考差异 中国女性在慌什么—Joy陈愉谈事业、爱情、婚姻 怎么说呐,大家放心 如何让“心理成长”帮助孩子更优秀 恋物癖:难以启齿的诱惑 金钱才是爱情的源泉 如果你够勇敢,此刻转个身,就能抱住你所爱的人

儿童为什么自伤? 我的意中人是个盖世英雄 家长观念中的误区 论止损 我为什么又爱又恨时尚杂志 混血宝宝知多少 《芈月传》告诉你:没把孩子管教好,成了赢家又如何? 一个国家和社会的品味其实是由女人决定的 我是歌手李健--时尚心理解析6 15岁挣到500万 时代病的白描 “张斌挺老虎伍兹引火烧身”说明了什么? 罗李华:金牛座2016年运势 “情绪识别”就是“安全教育” 低龄儿童培养的是领导力还是全能感? 就算翻脸,也别拔刀 论止损 什么是阿里巴巴的合伙人制度? 由租房话题说开了跑题话语。。。

编辑:马戏海

我要说两句: (0人参与)

发布