1_2、AQS前提知识之——LockSupport

作用:用于创建锁和其他同步类的基本线程阻塞原语,是一个线程阻塞工具类,所有方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根结底LockSupport调用的是Unsafe中的native代码

对比:

1、Synchronized:

阻塞:wait();

通知:notify();  notifyAll();

2、Lock

阻塞:await();

通知:signal();

Synchronized的阻塞通知缺点:

(1)wait和notify需要在Synchronized代码块里才能生效,否则会出现异常

(2)b线程要是先notify(),a线程再wait()。则程序会一直无法结束。

 1 static final Object objectLockA=new Object();
 2     public static void m1(){
 3         new Thread(()->{
 4 //            try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace(); }
 5             synchronized (objectLockA){
 6                 System.out.println(Thread.currentThread().getName() + "\t 进来");
 7                 try {
 8                     objectLockA.wait();
 9                 } catch (InterruptedException e) {
10                     e.printStackTrace();
11                 }
12                 System.out.println(Thread.currentThread().getName() + "\t 被唤醒");
13             }
14         }, "a").start();
15         new Thread(()->{
16             synchronized (objectLockA){
17                objectLockA.notify();
18                 System.out.println(Thread.currentThread().getName() + "\t 通知");
19             }
20         }, "b").start();
21     }
22 输出:
23 a     进来
24 b     通知
25 a     被唤醒
View Code

Lock的阻塞通知缺点

(1)await和signal需要在lock.lock()和lock.unlock()代码块里才能生效,否则会出现异常

(2)b线程要是先signal(),a线程再await()。则程序会一直无法结束。

 1     static Lock lock=new ReentrantLock();
 2     static Condition condition=lock.newCondition();
 3     public static void m1(){
 4         new Thread(()->{
 5 //            try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace(); }
 6             lock.lock();
 7             try {
 8                 System.out.println(Thread.currentThread().getName() + "\t 进来");
 9                 try {
10                     condition.await();
11                 } catch (InterruptedException e) {
12                     e.printStackTrace();
13                 }
14                 System.out.println(Thread.currentThread().getName() + "\t 被唤醒");
15             }finally {
16                 lock.unlock();
17             }
18         }, "a").start();
19         new Thread(()->{
20             lock.lock();
21             try {
22                 condition.signal();
23                 System.out.println(Thread.currentThread().getName() + "\t 通知");
24             }finally {
25                 lock.unlock();
26             }
27         }, "b").start();
28     }
View Code

 3、LockSupport:

阻塞:park()阻塞当前线程  park(Object blocker);阻塞传入的具体线程

调用LockSupport.park()时

public static void park(){
    UNSAFE.park(false,0L);
}

permit默认是0,所以一开始调用park()方法当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park()方法会被唤醒,然后会将permit再次设置为0并返回

通知:unpark(Thread thread);唤醒处于阻塞状态的指定线程

调用LockSupport.unpark(thread)时

public static void unpark(Thread thread){
    if(thread!=null){
    UNSAFE.unpark(thread);
    }
}

调用unpark()方法后,就会将thread线程的许可证permit设置成1(注意多次调用unpark方法,permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupper.park()方法会立即返回

 1     public static void m3() {
 2         Thread a = new Thread(() -> {
 3             try {
 4                 TimeUnit.SECONDS.sleep(3);
 5             } catch (InterruptedException e) {
 6                 e.printStackTrace();
 7             }
 8             System.out.println(Thread.currentThread().getName() + "\t 进来");
 9             LockSupport.park();
10             System.out.println(Thread.currentThread().getName() + "\t 被唤醒");
11         }, "a");
12         a.start();
13         new Thread(() -> {
14             LockSupport.unpark(a);
15             System.out.println(Thread.currentThread().getName() + "\t 通知");
16         }, "b").start();
17     }
View Code

 

理解:

线程阻塞需要消耗凭证(permit)才能通行。这个凭证最多只有1个,可用先unpark颁发凭证,再park消耗凭证。也就是类似可用先通知,后阻塞。

当调用park方法时。如果有凭证,则会直接消耗掉这个凭证然后正常执行退出。如果没有,就必须阻塞等待凭证可用。

而调用unpark方法时。会给阻塞线程颁发一个凭证,但凭证最后只能有1个,再次调用不会累加。

 

posted @ 2021-06-06 16:39  夜雨星辰丶  阅读(84)  评论(0)    收藏  举报