一起养成写作习惯!这是我参与「掘金日新计划 · 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 之的区别
- wait 和 notify 都是 Object 中的方法,在调用这两个方法前必须先获得锁对象,但是 park 不需要获取某个对象的锁就可以锁住线
- notify 只能随机选`择一个线程唤醒,无法唤醒指定的线程,unpark 却可以唤醒一个指定的线程