在Java多线程中,当需要阻塞或者唤醒一个线程时,都会使用LockSupport工具类来完成相应的工作。LockSupport定义了一组公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSupport也因此成为了构建同步组件的基础工具。
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程,而且park()和unpark()不会遇到“Thread.suspend 和 Thread.resume所可能引发的死锁”问题。
因为park() 和 unpark()有许可的存在;调用 park() 的线程和另一个试图将其 unpark() 的线程之间的竞争将保持活性。
实现的阻塞和解除阻塞是基于”许可(permit)”作为关联,permit相当于一个信号量(0,1),默认是0. 线程之间不再需要一个Object或者其它变量来存储状态,不再需要关心对方的状态.
在没有LockSupport之前,线程的挂起和唤醒咱们都是通过Object的wait和notify/notifyAll方法实现。
1.1、支持的函数
// 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
static Object getBlocker(Thread t)
// 为了线程调度,禁用当前线程,除非许可可用。
static void park()
// 为了线程调度,在许可可用之前禁用当前线程。
static void park(Object blocker)
// 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
static void parkNanos(long nanos)
// 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
static void parkNanos(Object blocker, long nanos)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(long deadline)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(Object blocker, long deadline)
// 如果给定线程的许可尚不可用,则使其可用。
static void unpark(Thread thread)
在Java 6中,LockSupport增加了park(Object blocker)、parkNanos(Object blocker, long nanos)、parkUntil(Object blocker, long deadline)这3个方法,用于实现阻塞当前线程的功能,其中参数blocker是用来标识当前线程在等待的对象,该对象主要用于问题排查和系统监控。下面的示例中,将对比parkNanos(long nanos)和parkNanos(Object blocker, long nanos)方法来展示阻塞对象blocker的用处。
采用parkNanos(long nanos)阻塞线程:
public class LockSupportTest {
public static void main(String[] args) {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(20));
可以通过命令:jstack -l pid查看dump
"main" #1 prio=5 os_prio=31 tid=0x00007fae82000000 nid=0x2603 waiting on condition [0x000070000eccb000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:338)
at com.github.bjlhx15.common.thread.juc.collection.LockSupportTest.main(LockSupportTest.java:8)
采用parkNanos(Object blocker, long nanos)阻塞线程:
public class LockSupportTest1 {
public static void main(String[] args) {
LockSupport.parkNanos(new Object(), TimeUnit.SECONDS.toNanos(20));
可以通过命令:jstack -l pid查看dump
"main" #1 prio=5 os_prio=31 tid=0x00007fb3b5813000 nid=0x2603 waiting on condition [0x000070000861d000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076acfb350> (a java.lang.Object)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at com.github.bjlhx15.common.thread.juc.collection.LockSupportTest1.main(LockSupportTest1.java:8)
这两段代码都是 阻塞当前线程20秒,从上面的dump结果可以看出,有阻塞对象的parkNanos方法能够传递给开发人员更多的现场信息。这是由于在Java 5之前,当线程使用synchronized关键字阻塞在一个对象上时,通过线程dump能够看到该线程的阻塞对象,而Java 5推出的Lock等并发工具却遗漏了这一点,致使在线程dump时无法提供阻塞对象的信息。因此,在Java 6中,LockSupport新增了上述3个含有阻塞对象的方法,用以替代原有的park方法。
通过源码可以发现,LockSupport的park和unpark方法都是通过sun.misc.Unsafe类的park和unpark方法实现的,那下面我们对sun.misc.Unsafe类的源码进行进一步解析。
1.2、LockSupport源码分析
从源码中得到初步信息:
1、不能被实例化(构造函数是私有的)
2、方法都是静态方法
3、核心方法实现基于Unsafe类中的park和unpark方法
1.2.1、UNSAFE
JDK内部用的工具类, 可以直接操控内存,被JDK广泛用于自己的包中.它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。
Unsafe类就和它的名字一样,是一个比较危险的类,它主要用于执行低级别、不安全的方法。尽管这个类和所有的方法都是公开的(public),但是这个类的使用仍然受限,你无法在自己的java程序中直接使用该类,因为只有授信的代码才能获得该类的实例。如果我们要使用Unsafe类,首先需要获取Unsafe类的对象,但是它的构造函数是private的:
private Unsafe() {}
只能通过Unsafe的getUnsafe()方法获取该类的对象:
@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
Unsafe类是比较危险的,它只有在授信代码中才会返回theUnsafe对象,否则,抛出SecurityException异常,那什么是授信的代码呢?我们看一看getClassLoader()方法:
@CallerSensitive
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
return cl;
该方法返回加载该类的类加载器,如果是被Bootstrap ClassLoader加载的类,则cl为null,然后我们再看VM.isSystemDomainLoader(ClassLoader)方法:
public static boolean isSystemDomainLoader(ClassLoader loader) {
return loader == null;
若类加载器为null,则返回true,即该代码为授信代码。所以,只要代码是被Bootstrap ClassLoader类加载器加载的类就是授信代码了。
我们知道Bootstrap ClassLoader类加载器会加载-Xbootclasspath参数所指定的路径中的类,所以,我们可以修改-Xbootclasspath参数,将我们的代码所在的路径添加进去,那我们的代码就可以使用Unsafe类了;或者也可以使用反射从 Unsafe类上得到它私有的Unsafe实例。如下所示:
public class UnsafeTest {
public static void main(String[] args) throws Exception {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
先看一下Unsafe的park方法和unpark方法:
public native void park(boolean isAbsolute, long time);
public native void unpark(Object thread);
这两个类都是声明native的,其具体实现要去看Java虚拟机的源代码,接下来看一看Unsafe类的其他方法,看一看Unsafe还能做什么危险操作。
1.2.2、Unsafe修改内存
Unsafe类的putInt()和getInt()方法可以直接修改内存,如下面这一段代码:
public static void main(String[] args) throws Exception {
unsafePutGetInt();
private static Unsafe getUnsafe(){
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
return unsafe;
} catch (Exception e) {
e.printStackTrace();
return null;
public static void unsafePutGetInt() throws Exception {
Unsafe unsafe = getUnsafe();
class Student {
private int age = 5;
public int getAge() {
return age;
Student student = new Student();
System.out.println(student.getAge());
Field field = student.getClass().getDeclaredField("age");
unsafe.putInt(student, unsafe.objectFieldOffset(field), 10);
System.out.println(student.getAge());
通过Unsafe直接修改了类的private变量值。类似的还有getBoolean()、putBoolean()、getChar()、putChar()等方法。
1.2.3、Unsafe在非Java堆中分配内存
使用new关键字分配的内存会在堆中,并且对象的生命周期内,会被垃圾回收器管理。Unsafe类通过allocateMemory(long)方法分配的内存,不受Integer.MAX_VALUE的限制,并且分配在非堆内存,使用它时,需要非常谨慎,该部分内存需要手动回收,否则会产生内存泄露;非法的地址访问时,会导致Java虚拟机崩溃。在需要分配大的连续区域、实时编程时,可以使用该方式,java的nio使用了这一方法。
public class UnsafeTest3 {
public static void main(String[] args) throws Exception {
unsafeAllocateMemory();
private static Unsafe getUnsafe(){
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
return unsafe;
} catch (Exception e) {
e.printStackTrace();
return null;
public static void unsafeAllocateMemory() throws Exception {
Unsafe unsafe = getUnsafe();
int BYTE = 1;
long address = unsafe.allocateMemory(BYTE);
unsafe.putByte(address, (byte) 10);
byte num = unsafe.getByte(address);
System.out.println(num);
unsafe.freeMemory(address);
1.2.4、Unsafe提供CAS原子操作
Unsafe类中提供了compareAndSwapObject()、compareAndSwapInt()和compareAndSwapLong()这三个方法用来实现对应的CAS原子操作。在Java的并发编程中用到的CAS操作都是调用的Unsafe类的相关方法。我们以Unsafe实现一个自定义原子类:
public class UnsafeTest4 {
public static void main(String[] args) throws Exception {
unsafeCAS();
private static Unsafe getUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
return unsafe;
} catch (Exception e) {
e.printStackTrace();
return null;
public static void unsafeCAS() throws Exception {
Unsafe unsafe = getUnsafe();
class MyAutomicInteger {
private volatile int value = 0;
private Unsafe unsafe;
private long offset;
public MyAutomicInteger(Unsafe unsafe) throws Exception {
this.unsafe = unsafe;
this.offset = unsafe.objectFieldOffset(MyAutomicInteger.class.getDeclaredField("value"));
public void increment() {
int oldValue = value;
for (;;) {
if (unsafe.compareAndSwapInt(this, offset, oldValue, oldValue + 1)) {
break;
oldValue = value;
public int getAndIncrement() {
int oldValue = value;
for (;;) {
if (unsafe.compareAndSwapInt(this, offset, oldValue, oldValue + 1)) {
return oldValue;
oldValue = value;
public int getValue() {
return value;
MyAutomicInteger myAutomicInteger = new MyAutomicInteger(unsafe);
myAutomicInteger.increment();
System.out.println(myAutomicInteger.getValue());
for (int i = 0; i < 5; i++) {
System.out.println(myAutomicInteger.getAndIncrement());
System.out.println(myAutomicInteger.getValue());
可以看到,通过Unsafe类可以实现很多有趣的功能,这些方法都是比较底层的方法,而且效率比较高,但是使用起来却比较危险,因为Unsafe类中的方法与我们通常的用法相悖,比如,通过Unsafe类直接修改其他类的parivate变量,直接分配堆外内存等等,这很像c语言的malloc()方法。在平常的开发当中,并不建议直接使用Unsafe类。
1.3、LockSupport的park(),unPark()与Thread的wait(),notify()区别
1.面向的主体不一样。LockSuport主要是针对Thread进进行阻塞处理,可以指定阻塞队列的目标对象,每次可以指定具体的线程唤醒。Object.wait()是以对象为纬度,阻塞当前的线程和唤醒单个(随机)或者所有线程。
2.实现机制不同。虽然LockSuport可以指定monitor的object对象,但和object.wait(),两者的阻塞队列并不交叉。
3.实现机制和wait/notify有所不同,面向的是线程
4.LockSupport不需要依赖监视器
5.与wait/notify没有交集
6.LockSupport使用起来方便灵活
LockSupport比Object的wait/notify有两大优势:
①LockSupport不需要在同步代码块里 。所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦。
②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序。
1.4、示例
1.4.1、使用Object的wait和notify
public static void objWaitNotify() throws InterruptedException {
final Object obj = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
try {
obj.wait();
} catch (Exception e) {
e.printStackTrace();
System.out.println(sum);
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
obj.notify();
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.github.bjlhx15.datastructure.algorithm.thread.LockSupportDemo$1.run(LockSupportDemo.java:18)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.github.bjlhx15.datastructure.algorithm.thread.LockSupportDemo.objWaitNotify(LockSupportDemo.java:28)
at com.github.bjlhx15.datastructure.algorithm.thread.LockSupportDemo.main(LockSupportDemo.java:5)
原因,wait和notify/notifyAll方法只能在同步代码块里用。所以将代码修改为如下就可正常运行了:
public static void objWaitNotifySynchronized() throws InterruptedException {
final Object obj = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
try {
synchronized (obj) {
obj.wait();
} catch (Exception e) {
e.printStackTrace();
System.out.println(sum);
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
synchronized (obj) {
obj.notify();
1.4.2、使用LockSupport
public static void lockSupport() throws InterruptedException {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
LockSupport.park();
System.out.println(sum);
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
LockSupport.unpark(A);
2.1、ThreadPoolExecutor中使用
public static void testFeture() throws ExecutionException, InterruptedException {
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(1000);
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,5,1000, TimeUnit.SECONDS,queue);
Future<String> future = poolExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(5);
return "hello";
String result = future.get();
System.out.println(result);
代码中我们向线程池中扔了一个任务,然后调用Future的get方法,同步阻塞等待线程池的执行结果。
问题1、get方法是如何组塞住当前线程?线程池执行完任务后又是如何唤醒线程的呢?
查看submit方法
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
在submit方法里,线程池将我们提交的基于Callable实现的任务,封装为基于RunnableFuture实现的任务,然后将任务提交到线程池执行,并向当前线程返回RunnableFutrue。
进入newTaskFor方法,就一句话:return new FutureTask<T>(callable);
所以,咱们主线程调用future的get方法就是FutureTask的get方法,线程池执行的任务对象也是FutureTask的实例。
接下来看看FutureTask的get方法的实现:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
判断下当前任务是否执行完毕,如果执行完毕直接返回任务结果,否则进入awaitDone方法阻塞等待。
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
LockSupport.parkNanos(this, nanos);
LockSupport.park(this);
awaitDone方法里,首先会用到上节讲到的cas操作,将线程封装为WaitNode,保持下来,以供后续唤醒线程时用。再就是调用了LockSupport的park/parkNanos组塞住当前线程。
提交的基于Callable实现的任务,已经被封装为FutureTask任务提交给了线程池执行,任务的执行就是FutureTask的run方法执行。如下是FutureTask的run方法:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
if (ran)
set(result);
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
c.call()就是执行我们提交的任务,任务执行完后调用了set方法,进入set方法发现set方法调用了finishCompletion方法,唤醒线程的工作就在这,
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
break;
done();
callable = null; // to reduce footprint
先是通过cas操作将所有等待的线程拿出来,然后便使用LockSupport的unpark唤醒每个线程。
三、实现原理
3.1、park
进入LockSupport的park方法,可以发现它是调用了Unsafe的park方法,这是一个本地native方法,只能通过openjdk的源码看看其本地实现了。
它调用了线程的Parker类型对象的park方法,如下是Parker类的定义:
类中定义了一个int类型的_counter变量,咱们上文中讲灵活性的那一节说,可以先执行unpark后执行park,就是通过这个变量实现,看park方法的实现代码
park方法会调用Atomic::xchg方法,这个方法会原子性的将_counter赋值为0,并返回赋值前的值。如果调用park方法前,_counter大于0,则说明之前调用过unpark方法,所以park方法直接返回。接着往下看:
实际上Parker类用Posix的mutex,condition来实现的阻塞唤醒。如果对mutex和condition不熟,可以简单理解为mutex就是Java里的synchronized,condition就是Object里的wait/notify操作。
park方法里调用pthread_mutex_trylock方法,就相当于Java线程进入Java的同步代码块,然后再次判断_counter是否大于零,如果大于零则将_counter设置为零。最后调用pthread_mutex_unlock解锁,相当于Java执行完退出同步代码块。如果_counter不大于零,则继续往下执行pthread_cond_wait方法,实现当前线程的阻塞。
最后再看看unpark方法的实现吧,这块就简单多了,直接上代码:
图中的1和4就相当于Java的进入synchronized和退出synchronized的加锁解锁操作,代码2将_counter设置为1,同时判断先前_counter的值是否小于1,即这段代码:if(s<1)。如果不小于1,则就不会有线程被park,所以方法直接执行完毕,否则就会执行代码3,来唤醒被阻塞的线程。
通过阅读LockSupport的本地实现,我们不难发现这么个问题:多次调用unpark方法和调用一次unpark方法效果一样,因为都是直接将_counter赋值为1,而不是加1。简单说就是:线程A连续调用两次LockSupport.unpark(B)方法唤醒线程B,然后线程B调用两次LockSupport.park()方法, 线程B依旧会被阻塞。因为两次unpark调用效果跟一次调用一样,只能让线程B的第一次调用park方法不被阻塞,第二次调用依旧会阻塞。