
JUC-04_LockSupport和线程调度
-
AQS的两大核心类,Unsafe(提供CAS),LockSupport(提供线程调度语义支持,等待-通知模式)
-
wait-notify | park-unpark
-
LockSupport.unpark()会释放锁资源吗,Condition.await()呢
-
Thread.sleep(),Object.wait(),Condition.await(),LockSupport.unpark()的区别
-
wait()前就执行notify()会怎么样?
-
park()前就执行unpark()会怎么样?
LockSupport
LockSupport提供了创建锁和同步器的基本的线程阻塞语义,翻译成人话就是一套等待-通知的规则,平替Object类中的wait() notify()
其实是扩展了Unsafe类的park和unpark方法
Unsafe类中:
-
native park(boolean isAbsolute,long time),isAb是否绝对时间,time超时时间ms,超时后释放
在下列情况之前,都会被阻塞
-
调用unpark函数,释放线程的许可
-
线程被中断
-
达到超时时间,绝对时间true,为false时,time0为无限等待
-
-
native unpark(Thread t),释放线程的许可,不安全,调用方需要保证线程依旧存活
LockSupport中核心
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
// blocker用于追溯是哪个线程来进行的park
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
// 底层就是调用的Unsafe的park
public static void park() {
UNSAFE.park(false, 0L);
}
// 阻塞多少时间后释放,相对时间
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
// 指定到某个时间释放,绝对时间
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
// 如果指定线程的许可不可用,让它可用,如果线程上受阻塞,让他解除阻塞,如果线程没阻塞,保证它下一次park不会阻塞,如果线程没启动,不保证安全性
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
前后两次setBlocker是为了保证下次调用park时不会读取到上一个blocker。
wait-notify使用:
package com.kaguya.demo;
public class Thread1 {
public static void main(String[] args) throws InterruptedException {
MThd thread = new MThd();
synchronized (thread) {
thread.start();
System.out.println("wait前");
thread.wait();
System.out.println("wait后");
}
}
}
class MThd extends Thread {
@Override
public void run() {
synchronized (this) {
System.out.println("notify前");
// 睡眠3s模拟执行业务,执行完毕通知主线程
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
notify();
System.out.println("notify后");
}
}
}
park-unpark使用:
package com.kaguya.demo;
import java.util.concurrent.locks.LockSupport;
public class Thread2_Park {
public static void main(String[] args) {
Thread currentThread = Thread.currentThread();
// 暴露自己给线程,方便它知道通知谁。
MyThd myThd = new MyThd(currentThread);
myThd.start();
System.out.println("主线程park,等待线程执行结束调用unpark通知");
LockSupport.park(myThd.hashCode());
System.out.println("主线程此次park流程结束,查询blocker:"+ LockSupport.getBlocker(currentThread));
}
}
class MyThd extends Thread {
Thread t;
public MyThd(Thread t) {
this.t =t;
}
@Override
public void run() {
System.out.println("unpark前,执行业务,");
try {
// 睡眠模拟执行业务
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("unpark前blocker:"+ LockSupport.getBlocker(t));
LockSupport.unpark(t);
System.out.println("unpark后blocker:"+ LockSupport.getBlocker(t));
}
}
- 注意park方需要告知unpark方
Thread.sleep() 和 Object.wait()区别
-
sleep()不会释放已经占有的锁,wait()会释放锁
-
sleep()必需传入时间,wait()不传时间是无限期等待
-
sleep()到了会自动唤醒,然后继续执行
-
wait()不带时间,需要其他线程notify()唤醒
-
wait()带时间,没有被notify(),到了过期时间,需要notify()方释放锁,且wait()方获取到了对象的锁才会唤醒,否则进入同步队列等待。
sleep()和wait()主要区别是一个不会释放锁,一个会释放锁,剩下的点都是二者用法上的差别
Object.wait()和Condition.await()的区别
-
都会释放锁资源
-
Obj.wait() jdk层面
-
Condition.await() 基于LockSupport.park()实现
Condition.await()在阻塞前:1、当前线程加入到条件队列 2、完全释放锁,state变量为0
Thread.sleep()和LockSupport.park()
都是阻塞当前方法,且阻塞时不会释放当前线程占有的锁资源
-
Thread.sleep()
-
原生native方法
-
调用需要捕获或抛出InterruptedException异常
-
只能自己醒过来,无法被外部唤醒
-
-
LockSupport.park()
-
底层调用的Unsafe的native方法
-
不需要捕获中断异常
-
可以被另一个线程调用LockSupport.unpark()唤醒
-
Object.wait()和LockSupport.park()
-
obj.wait()
-
必需上锁才能用(否则抛出IllegalMonitorStateException非法监视器状态异常)
-
阻塞释放锁资源
-
发生中断会抛异常
-
需要notify()唤醒,不一定会立即执行后续内容
-
顺序要求先wait()后notify(),否则notify()失效
-
-
LS.park()
-
不需要上锁
-
阻塞不释放锁资源
-
发生中断等同unpark(),唤醒
-
配合unpark(),后续内容立即执行
-
可以先unpark()后park(),此时unpark()保证下一个park()不会被阻塞(只是提前获得许可罢了)
-