Java多线程基础之LockSupport八连问

135 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

为什么 LockSupport 是核心基础类?

首先 LockSupport 是锁中的基础,是一个提供锁机制的工具类

并发组件和并发工具大都是基于 AQS 来实现的,而 AQS 中的控制线程又是通过 LockSupport 类来实现的,因此可以说,LockSupport 是 Java 并发基础组件中的基础组件

Java 并发组件和并发工具类如下:

  • 并发组件:线程池、阻塞队列、Future 和 FutureTask、Lock 和 Condition
  • 并发工具:CountDownLatch、CyclicBarrier、Semaphore 和 Exchanger

写出分别通过 wait/notify 和 LockSupport 的 park/unpark 实现同步?

使用 park()/unpark():

static class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程开始执行,随后阻塞线程");
        LockSupport.park();
        System.out.println("线程执行结束");
    }
}

public static void main(String[] args) throws InterruptedException {
    MyThread myThread = new MyThread();
    myThread.start();
    LockSupport.unpark(myThread);
    System.out.println("解锁线程");
}

使用 wait()/notify():

static class MyThread extends Thread {
    @Override
    public void run() {
        //对当前对象加锁
        synchronized (this){
            System.out.println("线程开始执行,随后阻塞线程");
            notify();
            System.out.println("线程执行结束");
        }
    }
}

public static void main(String[] args) throws InterruptedException {
    MyThread myThread = new MyThread();
    //对myThread对象加锁
    synchronized (myThread) {
        myThread.start();
        //主线程休眠三秒
        Thread.sleep(3000);
        System.out.println("进入等待前");
        //这里释放锁,让run方法执行,然后notify方法会唤醒阻塞线程,打印最后一句执行结束
        myThread.wait();
        System.out.println("执行结束");
    }
}

LockSupport.park() 会释放锁资源吗?

不会,它只负责阻塞当前线程,释放锁资源实际上是在 Condition 的 await()方法中实现的

Thread.sleep()、Object.wait()、Condition.await()、LockSupport.park()的区别?

LockSupport.park()还有几个兄弟方法——parkNanos()、parkUtil()等,我们这里说的 park() 方法统称这一类方法

Thread.sleep() 和 Object.wait() 的区别

  • Thread.sleep() 不会释放占有的锁,Object.wait() 会释放占有的锁
  • Thread.sleep() 必须传入时间,Object.wait() 可传可不传,不传表示一直阻塞下去
  • Thread.sleep() 到时间了会自动唤醒,然后继续执行
  • Object.wait() 不带时间的,需要另一个线程使用 Object.notify() 唤醒
  • Object.wait() 如果传入了时间,假如没有被 notify,到时间了会自动唤醒,这时又分两种情况,一是立即获取到了锁,线程自然会继续执行;二是没有立即获取锁,线程进入同步队列等待获取锁

其实,他们俩最大的区别就是 Thread.sleep() 不会释放锁资源,Object.wait() 会释放锁资源

Thread.sleep() 和 Condition.await() 的区别

Object.wait() 和 Condition.await() 的原理是基本一致的,不同的是 Condition.await() 底层是调用 LockSupport.park() 来实现阻塞当前线程的

实际上,Condition.await() 在阻塞当前线程之前还干了两件事,一是把当前线程添加到条件队列中,二是“完全”释放锁,也就是让state状态变量变为0,然后才是调用LockSupport.park()阻塞当前线程

Thread.sleep() 和 LockSupport.park() 的区别

  • 从功能上来说,Thread.sleep()和LockSupport.park()方法类似,都是阻塞当前线程的执行,且都不会释放当前线程占有的锁资源
  • Thread.sleep() 没法从外部唤醒,只能自己醒过来;
  • LockSupport.park() 方法可以被另一个线程调用 LockSupport.unpark() 方法唤醒
  • Thread.sleep() 方法声明上抛出了InterruptedException中断异常,所以调用者需要捕获这个异常或者再抛出
  • LockSupport.park() 方法不需要捕获中断异常
  • Thread.sleep() 本身就是一个native方法
  • LockSupport.park() 底层是调用的 Unsafe 的 native 方法

Object.wait() 和 LockSupport.park() 的区别

二者都会阻塞当前线程的运行,他们有什么区别在于:

  • Object.wait() 方法需要在 synchronized 块中执行

  • LockSupport.park() 可以在任意地方执行

  • Object.wait() 方法声明抛出了中断异常,调用者需要捕获或者再抛出

  • LockSupport.park() 不需要捕获中断异常

  • Object.wait() 不带超时的,需要另一个线程执行 notify() 来唤醒,但不一定继续执行后续内容

  • LockSupport.park() 不带超时的,需要另一个线程执行 unpark() 来唤醒,一定会继续执行后续内容

  • 如果在wait()之前执行了notify()会怎样?

    • 抛出 IllegalMonitorStateException异常
  • 如果在park()之前执行了unpark()会怎样?

    • 线程不会被阻塞,直接跳过park(),继续执行后续内容

park()/unpark() 和 wait/notify 之的区别

  1. wait 和 notify 都是 Object 中的方法,在调用这两个方法前必须先获得锁对象,但是 park 不需要获取某个对象的锁就可以锁住线
  2. notify 只能随机选`择一个线程唤醒,无法唤醒指定的线程,unpark 却可以唤醒一个指定的线程