LockSupport

LockSupport

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport中的park()和unpark()的作用分别是阻塞线程和解除阻塞线程。

3种让线程等待和唤醒的方法

  1. 使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程
  2. 使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程
  3. LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

1. 使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程

// 正常情况下
static Object objectLock = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+"----被唤醒");
            }
        },"A").start();

        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"\t"+"----唤醒动作");
            }
        },"B").start();
    }
//输出结果
A	----come in
B	----唤醒动作
A	----被唤醒
//异常情况1:wait()和notify()脱离synchronized单独使用。
static Object objectLock = new Object();

    public static void main(String[] args) {
        new Thread(()->{
//            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+"----被唤醒");
//            }
        },"A").start();

        new Thread(()->{
//            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"\t"+"----唤醒动作");
//            }
        },"B").start();
    }
//输出结果
java.lang.IllegalMonitorStateException
//异常情况2:先notify(),后wait(),A线程一直处于等待状态。
static Object objectLock = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            //暂停几秒钟,确保B线程先进入。
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+"----被唤醒");
            }
        },"A").start();

        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"\t"+"----唤醒动作");
            }
        },"B").start();
    }
//输出结果
B	----唤醒动作
A	----come in

结论:

  1. Object类中的wait、notify、notifyAll用于线程等待和唤醒的方法,都必须在sychronized内部执行(必须用到关键字sychronized),且成对出现使用
  2. 先wait,再notify才可以。

2. 使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程

//正常情况下
static Object objectLock = new Object();
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) {
        new Thread(()->{
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
                condition.await();
                System.out.println(Thread.currentThread().getName()+"\t"+"----被唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        },"A").start();

        new Thread(()->{
            lock.lock();
            try{
                condition.signal();
                System.out.println(Thread.currentThread().getName()+"\t"+"----唤醒动作");
            }finally{
                lock.unlock();
            }
        },"B").start();
    }
//输出情况
A	----come in
B	----唤醒动作
A	----被唤醒
//异常情况1:注销lock()和unlock()
static Object objectLock = new Object();
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) {
        new Thread(()->{
//            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
                condition.await();
                System.out.println(Thread.currentThread().getName()+"\t"+"----被唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
//                lock.unlock();
            }
        },"A").start();

        new Thread(()->{
//            lock.lock();
            try{
                condition.signal();
                System.out.println(Thread.currentThread().getName()+"\t"+"----唤醒动作");
            }finally{
//                lock.unlock();
            }
        },"B").start();
    }
//输出结果报异常
java.lang.IllegalMonitorStateException
//异常情况2:线程B先进入,即先signal(),后await()
static Object objectLock = new Object();
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) {
        new Thread(()->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
                condition.await();
                System.out.println(Thread.currentThread().getName()+"\t"+"----被唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        },"A").start();

        new Thread(()->{
            lock.lock();
            try{
                condition.signal();
                System.out.println(Thread.currentThread().getName()+"\t"+"----唤醒动作");
            }finally{
                lock.unlock();
            }
        },"B").start();
    }
//输出结果
B	----唤醒动作
A	----come in

传统的synchronized和Lock实现等待唤醒通知的约束:

  1. 线程需要获得并持有锁,必须在锁块(synchronized或lock中)
  2. 必须要先等待后唤醒,线程才能够被唤醒。

3. LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

//不需要在锁块中
    public static void main(String[] args) {
        Thread a = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "----come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t" + "----被唤醒");
        }, "A");
        a.start();
        Thread b = new Thread(() -> {
            LockSupport.unpark(a);
            System.out.println(Thread.currentThread().getName() + "\t" + "----唤醒动作");
        }, "B");
        b.start();
    }
//输出结果
A	----come in
B	----唤醒动作
A	----被唤醒
//先执行unpark(),依然可以被唤醒
public static void main(String[] args) {
        Thread a = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "\t" + "----come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t" + "----被唤醒");
        }, "A");
        a.start();
        Thread b = new Thread(() -> {
            LockSupport.unpark(a);
            System.out.println(Thread.currentThread().getName() + "\t" + "----唤醒动作");
        }, "B");
        b.start();
    }
//输出结果
B	----唤醒动作
A	----come in
A	----被唤醒

总结
LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根揭底,LockSupport调用的是Unsafe中的native代码。

  1. LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit)。
  2. permit只有两个值1和0,默认是0。
  3. 可以把许可看成是一种(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。
  4. permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park()方法会被唤醒,然后会将permit再次设置为0并返回。

4. 面试题

为什么可以先唤醒线程后阻塞线程
-----因为unpark获取了一个凭证,之后再调用park方法,就可以名正言顺的凭证消费,故不会阻塞。
为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?
-----因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证;而调用两次park却需要消费两个凭证,证不够,不能放行。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值