目录
1.概述
2.LockSupport的park-unpark与Object的wait-notitfy的对比
3.LockSupport的源码分析
3.1 成员变量
3.2 park方法
3.3 unpark方法
在没有LockSupport之前,线程的挂起和唤醒咱们都是通过Object的wait和notify/notifyAll方法实现。
wait-notify代码案例1:
package LockSupport;
public class TestObjWait {
public static void main(String[] args)throws Exception {
final Object obj = new Object();
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);
}
}).start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
obj.notify();
}
}
wait-notify代码案例1改进:
package LockSupport;
public class TestObjWait {
public static void main(String[] args)throws Exception {
final Object obj = new Object();
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);
}
}).start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
synchronized (obj){
obj.notify();
}
}
}
当我们换成LockSupport的park和unpark后,如下
park-unpark代码案例1:
package LockSupport;
import java.util.concurrent.locks.LockSupport;
public class TestLockSupport {
public static void main(String[] args)throws Exception {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
try {
//注意:LockSupport中所有的方法都是static的,所以直接使用类来调用即可
LockSupport.park();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(sum);
}
});
thread.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在park方法
Thread.sleep(1000);
LockSupport.unpark(thread);
}
}
wait-notify代码案例2:
package LockSupport;
public class TestObjWait {
public static void main(String[] args)throws Exception {
final Object obj = new Object();
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);
}
}).start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
//Thread.sleep(1000);
synchronized (obj){
obj.notify();
}
}
}
而LockSupport就支持主线程先调用unpark后,线程A再调用park而不被阻塞
park-unpark代码案例2:
package LockSupport;
import java.util.concurrent.locks.LockSupport;
public class TestLockSupport02 {
public static void main(String[] args)throws Exception {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
try {
//注意:LockSupport中所有的方法都是static的,所以直接使用类来调用即可
LockSupport.park();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(sum);
}
});
thread.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在park方法
//Thread.sleep(1000);
LockSupport.unpark(thread);
}
}
总结:LockSupport比Object的wait/notify的优势:
public class LockSupport {
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
// 表示内存偏移地址
private static final long parkBlockerOffset;
// 表示内存偏移地址
private static final long SEED;
// 表示内存偏移地址
private static final long PROBE;
// 表示内存偏移地址
private static final long SECONDARY;
static {
try {
// 获取Unsafe实例
UNSAFE = sun.misc.Unsafe.getUnsafe();
// 线程类类型
Class> tk = Thread.class;
// 获取Thread的parkBlocker字段的内存偏移地址
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
// 获取Thread的threadLocalRandomSeed字段的内存偏移地址
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
// 获取Thread的threadLocalRandomProbe字段的内存偏移地址
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
// 获取Thread的threadLocalRandomSecondarySeed字段的内存偏移地址
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
}
关于SEED和PROBE的作用可以看我的另一篇博客:https://blog.csdn.net/qq_34805255/article/details/102384212
在我们第一次主动使用LockSupport类(如调用LockSupport的某个静态方法)的时候,进行类的初始化的时候,会执行上述代码
首先说明Thread类中有个parkBlocker字段,就是会在park方法中使用
park方法横向的其实有两种
我们来对比一下它们的源码:
可以发现,
那么设置此Blocker有什么作用呢?
为什么要前后调用两次setBlocker呢?
示例:
没有传入Blocker:
package LockSupport;
import java.util.concurrent.locks.LockSupport;
public class TestPark {
public static void main(String[] args) {
LockSupport.park();
}
}
使用jps和jstack工具配合查看
当传入Blocker:
package LockSupport;
import java.util.concurrent.locks.LockSupport;
public class TestPark {
public static void main(String[] args) {
TestPark testPark = new TestPark();
LockSupport.park(testPark);
}
}
我们接下来再纵向的比较一下三种不同参数的park方法
1.park()
需要注意的是调用park()方法被阻塞的线程被其他线程中断后阻塞线程返回时候并不会抛出InterruptedException 异常。
示例:
package LockSupport;
import java.util.concurrent.locks.LockSupport;
/**
* 调用park方法的线程只有中断才能使park方法返回
*/
public class LockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程 park start!");
// 调用park方法,挂起自己,只有中断才会退出循环
while (!Thread.currentThread().isInterrupted()) {
LockSupport.park();
}
System.out.println("子线程 退出!");
}
});
//启动子线程
thread.start();
//主线程休眠1S
Thread.sleep(1000);
System.out.println("主线程 interrupt start!");
//中断子线程
thread.interrupt();
}
}
2.parkNanos(long nanos)
3.parkUntil(long deadline)
示例1:
package LockSupport;
import java.util.concurrent.locks.LockSupport;
public class LockSupportTest {
public static void main( String[] args ) {
System.out.println( "park start!" );
//使当前线程获取到许可证
LockSupport.unpark(Thread.currentThread());
//再次调用park
LockSupport.park();
System.out.println( "park stop!" );
}
}
示例2:
package LockSupport;
import java.util.concurrent.locks.LockSupport;
public class LockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程 park start!");
//由于线程默认没有许可,调用park方法,挂起自己
LockSupport.park();
System.out.println("子线程 unpark!");
}
});
//启动子线程
thread.start();
//主线程休眠1S
Thread.sleep(1000);
System.out.println("主线程 unpark start!");
//调用unpark让thread线程持有许可证,然后park方法会返回
LockSupport.unpark(thread);
}
}
本文参考:
https://www.cnblogs.com/qingquanzi/p/8228422.html
https://blog.csdn.net/liangwenmail/article/details/82151614