Android LockSupport

1.LockSupport

LockSupport是一个线程阻塞工具类,用于阻塞和唤醒线程。它的所有方法都是静态的,可以在线程执行的任意位置调用它,使用LockSupport.park()方法使线程阻塞,然后在需要唤醒线程的地方调用LockSupport.unpark()方法解除阻塞。

LockSupport类其实使用了类似信号量的东西,称为许可permit。许可将LockSupport和线程关联起来。

每个线程都有一个许可,在一个线程中调用LockSupport.park()方法时,如果这个线程的许可是可用的,就把许可设置成不可用,同时线程进入阻塞。调用LockSupport.unpark(t1)方法时,需要传入一个线程作为参数,如果此时该线程的许可不可用,则将它的许可设置成可用,同时该线程被唤醒,不再阻塞。简单说就是,调用park会消耗许可,调用unpark会释放许可。

注意:

①每个线程都只有一个许可,重复调用unpark许可也不会积累。比如线程B连续调用三次unpark函数,当线程A调用park函数时就会使用掉这个许可,如果线程A再次调用park,就会进入等待状态。

②unpark()可以先于park()调用。比如线程B调用unpark函数给线程A发了一个许可,那么当线程A调用park时,它发现已经有许可了,它会马上再继续运行。

③如果调用park的线程被中断intercepted,则park方法会返回,此时无需调用unpark即可执行park后面的代码(即此时interrupt起到的作用与unpark一样)。

④unpark()方法以Thread为参数,可以更精准地唤醒某一个线程。

由于许可的存在,park()和unpark(t1)方法不会有Thread.suspend和Thread.resume可能引发的死锁问题。这样调用park的线程和另一个试图将其unpark的线程之间的竞争将保持活性。

 

2.LockSupport的用法

LockSupport的用法和wait/notify很类似,但是它比wait/notify更灵活。

class MainActivity : AppCompatActivitya {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        var t1: MyThread = MyThread()

        t1.start()

        Thread.sleep(1000) //保证子线程先运行

        Log.i(TAG, "子线程已经启动,并在内部进行了park")

        LockSupport.unpark(t1)

        Log.i(TAG, "主线程进行了unpark")

    }

   class MyThread: Thread {

        overrido fun run() {

            Log.i(TAG, name + " 进入子线程");

            LockSupport.park()

            Log.i(TAG, name + "子线程运行结束");

        }

    }   

}

首先定义了一个线程,并在线程内部调用park方法,然后在main线程中调用unpark方法来唤醒线程继续执行。

打印结果:

Thread-1进入子线程

子线程已经启动,并在内部进行了park

主线程进行了unpark

Thread-1子线程运行结束

 

注意看LockSupport和wait/notify的区别,主要有三点:

①wait/notify是Object中的方法,在调用这两个方法前必须先获得锁对象(wait/notify必须在synchronized同步代码块中使用),但是park不需要获取某个对象的锁就可以锁住线程。

②notify只能随机选择一个线程唤醒,无法唤醒指定的线程,而unpark却可以唤醒一个指定的线程。

③使用wait/notify实现同步时,必须先调用wait后调用notify,若先调用notify后调用wait,将会一直阻塞。而park/unpark调用不分先后,更加灵活。

 

3.LockSupport源码

LockSupport实际上调用了Unsafe类里的函数,Unsafe是一个非常强大的类,它的操作是基于底层的,也就是可以直接操作内存。

Unsafe里两个重要的函数:

1)public native void unpark(Thread jthread);

2)public native void park(boolean isAbsolute, long time); 

LockSupport就是引入了Unsafe类中的park()和unpark()方法,park()方法中的Parker类中有一个成员变量_counter就代表了许可。调用park()方法时,如果_counter大于0(即许可可用),就把它设置为0,并且调用Linux线程下的pthread_cond_timedwait一直等待;调用unpark()方法时,如果_counter等于0(即许可不可用),就把它设置为1,然后直接返回,否则就一直等待许可的改变,这样就实现了park()和unpark()方法之间的活性竞争。

现在来看LockSupport的源码:

public class LockSupport {

    private LockSupport() { }

    private static void setBlocker(Thread t, Object arg) {

        UNSAFE.putObject(t, parkBlockerOffset, arg);

    }

    /**返回提供给最近一次尚未解除阻塞的park方法调用的blocker对象。如果该调用不受阻塞,则返回null。返回的值只是一个瞬间快照,即由于未解除阻塞或者在不同的blocker对象上受阻而具有的线程。*/

    public static Object getBlocker(Thread t) {

        if (t == null)

            throw new NullPointerException();

        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);

    }

    /**如果给定线程的许可尚不可用,该方法会使其可用。即如果线程在park上受阻塞,它将解除其阻塞状态。否则,保证下一次调用park不会受阻塞。如果给定线程尚未启动,则无法保证此操作有任何效果。 */

    public static void unpark(Thread thread) {

        if (thread != null)

            UNSAFE.unpark(thread);

    }

    /** 为了线程调度,在许可可用之前阻塞当前线程。 如果许可可用,则使用该许可,并且该调用立即返回;否则,为线程调度禁用当前线程,并在发生以下三种情况之一以前,使其处于休眠状态:

     1. 其他某个线程将当前线程作为目标调用了 unpark方法

     2. 其他某个线程中断当前线程

     3. 该调用不合逻辑地(即毫无理由地)返回

     */

    public static void park() {

        UNSAFE.park(false, 0L);

    }

    /**同park()方法,增加了等待的相对时间*/

    public static void parkNanos(long nanos) {

        if (nanos > 0)

            UNSAFE.park(false, nanos);

    }

    /**同park()方法,增加了等待的绝对时间 */

    public static void parkUntil(long deadline) {

        UNSAFE.park(true, deadline);

    }

    /**同park()方法,增加了暂停的同步对象*/

    public static void park(Object blocker) {

        Thread t = Thread.currentThread();

        setBlocker(t, blocker);

        UNSAFE.park(false, 0L);

        setBlocker(t, null);

    }

    /**同parkNanos(long nanos)方法,增加了暂停的同步对象  */

    public static void parkNanos(Object blocker, long nanos) {

        if (nanos > 0) {

            Thread t = Thread.currentThread();

            setBlocker(t, blocker);

            UNSAFE.park(false, nanos);

            setBlocker(t, null);

        }

    }

    /**同parkUntil(long deadline)方法,增加了暂停的同步对象*/

    public static void parkUntil(Object blocker, long deadline) {

        Thread t = Thread.currentThread();

        setBlocker(t, blocker);

        UNSAFE.park(true, deadline);

        setBlocker(t, null);

    }

}

 

4.几个例子

看完LockSupport的源码,来看几个例子验证一下。

①先park再unpark

class MainActivity: AppCompatActivity {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        var t: Thread = Thread(MyRunnable())

        t.name = "A-name"

        t.start()

        Thread.sleep(30000) //主线程做自己的事情

        LockSupport.unpark(t)

        Log.i(TAG, "天亮了,妈妈喊我起床")

    }

    inner class MyRunnable: Runnable {

        override fun run() {

            var a: String = "A"

            Log.i(TAG, "天黑,我要睡觉了")

            LockSupport.park(a)

            Log.i(TAG, "我起床了")

        }

    }

}

输出结果:

天黑,我要睡觉了

天亮了,妈妈喊我起床

我起床了

在等待park的过程中,可以用查看是否能够打印出导致阻塞的对象A,找到A-Name这个线程确实看到了等待一个String对象:

"A-Name" #11 prio=5 os_prio=31 tid=0x00007fc143009800 nid=0xa803 waiting on condition [0x000070000c233000]

   java.lang.Thread.State: WAITING (parking)

        at sun.misc.Unsafe.park(Native Method)

        - parking to wait for <0x000000076adf4d30> (a java.lang.String)

        at java.util.concurrent.locks.LockSuppo rt.park(LockSupport.java:175)

②先interrupt再park

class MainActivity: AppCompatActivity {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        var t: Thread = Thread(MyRunnable())

        t.name = "A-name"

        t.start()

        Thread.sleep(1000)

        Log.i(TAG, "半夜,突然肚子疼")

        t.interrupt()

        Thread.sleep(30000) //主线程做自己的事情

        LockSupport.unpark(t)

        Log.i(TAG, "天亮了,妈妈喊我起床")

    }

    inner class MyRunnable: Runnable {

        override fun run() {

            var a: String = "A"

            Log.i(TAG, "天黑,我要睡觉了")

            LockSupport.park(a)

            Log.i(TAG, "我肚子疼,起床了")

            Log.i(TAG, "是否中断:" + Thread.currentThread().isInterrupted)

        }

    }

}

输出结果:

天黑,我要睡觉了

半夜,突然肚子疼

我肚子疼,起床了

是否中断:true

天亮了,妈妈喊我起床了

可以看到线程park后,如果遇到中断,并不会抛出InterruptedException,而是会继续执行park后面的代码。也就是此时interrupt起到的作用和unpark一样。

③先unpark再park

class MainActivity: AppCompatActivity {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        var t: Thread = Thread(MyRunnable())

        t.start()

        LockSupport.unpark(t)

        Log.i(TAG, "提前上好闹钟7点起床")

    }

    inner class MyRunnable: Runnable {

        override fun run() {

            Thread.sleeping(1000) //保证让主线程先执行unpark方法

            var a: String = "A"

            Log.i(TAG, "天黑,我要睡觉了")

            LockSupport.park(a)

            Log.i(TAG, "7点到,我起床了")

        }

    }

}

按照上面说过的,先调用unpark设置好许可,然后再调用park获取许可的时候就不会进行等待了。

打印结果:

提前上好闹钟7点起床

天黑,我要睡觉了

7点到,我起床了

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值