是什么?
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),
permit只有两个值1和零,默认是零。
可以把许可看成是一种(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。
通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作
使用:
API:
阻塞:
park() / park(Object blocker)
阻塞当前线程/阻塞传入的具体线程
Park源码:
//java.util.concurrent.locks.LockSupport
public static void park() {
UNSAFE.park(false, 0L);
}
可以看到LockSupport源码调用了UNSAFE类的park,是被native标记的
//sun.misc.Unsafe
public native void park(boolean var1, long var2);
permit许可证默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的
permit设置为1时,park方法会被唤醒,
然后会将permit再次设置为0并返回。
唤醒:
unpark(Thread thread)
唤醒处于阻断状态的指定线程
unpark源码:
//java.util.concurrent.locks.LockSupport
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
可以看到LockSupport源码调用了UNSAFE类的unpark,是被native标记的
//sun.misc.Unsafe
public native void unpark(Object var1);
调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,permit值还是1)会自动唤醒thread线程,
即之前阻塞中的LockSupport.park()方法会立即返回。
注意:
LockSupport:俗称 锁中断
以前的两种方式:
1.以前的等待唤醒通知机制必须synchronized里面有一个wait和notify
2.lock里面有await和signal
这上面这两个都必须要持有锁才能干,
LockSupport它的解决的痛点
1。LockSupport不用持有锁块,不用加锁,程序性能好,
2。先后顺序,不容易导致卡死
不用加锁:
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t ----begi"+System.currentTimeMillis());
LockSupport.park();//阻塞当前线程
System.out.println(Thread.currentThread().getName() + "\t ----被唤醒"+System.currentTimeMillis());
}, "t1");
t1.start();
LockSupport.unpark(t1);//唤醒
System.out.println(Thread.currentThread().getName()+"\t 通知t1...");
不分先后顺序,但不累加:
park()和unpark()不分先后顺序:
Thread t1 = new Thread(() -> {
//第一步:👇👇👇线程进来先睡眠
try { TimeUnit.SECONDS.sleep(5L); }catch (Exception e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName() + "\t ----begi"+System.currentTimeMillis());
//第三步:现在线程继续执行,并且通行证已经变为了1
LockSupport.park();//阻塞当前线程
//执行到👆通行证由1变为0,相当于不阻塞,直接忽略
//⚡⚡⚡⚡⚡⚡⚡⚡⚡先执行的unpark(t1)让这里形同虚设⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
System.out.println(Thread.currentThread().getName() + "\t ----被唤醒"+System.currentTimeMillis());
}, "t1");
t1.start();
try { TimeUnit.SECONDS.sleep(1); }catch (Exception e) {e.printStackTrace();}
//第二步:t1线程在睡眠过程中发放通行证,由 0变为1 ⚡⚡⚡
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName()+"\t 通知t1...");
}
说明
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根结底,LockSupport调用的Unsafe中的native代码。
LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,
调用一次unpark就加1变成1
(如果连续多次unpark也只会变为1不会累加,因为permit只有两个值1和零,默认是零。)
调用一次park会消费permit,也就是将1变成o,同时park立即返回。
如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1。
每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累凭证。
简单说就是:
线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。
当调用park方法时
*如果有凭证,则会直接消耗掉这个凭证然后正常退出;
*如果无凭证,就必须阻塞等待凭证可用;
而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效。