LockSupport--线程等待唤醒工具类

LockSupport

为什么要学习LockSupport

java--JVM
JUC--AQS(前置知识:可重入锁,LockSupport)

LockSupport是什么?

一句话解释:是线程等待唤醒机制(wait/notify)的增强版本
LockSupport中的park()unpark()的作用分别是阻塞线程和解除阻塞线程

生产者消费者模式--传统版本

3.0 LockSupport有哪些优势

三种线程等待和唤醒的方法

  • 使用Object中的wait()--notify()方法来阻塞、唤醒线程
  • 使用JUC包中Conditionawait()--signal()方法来阻塞、唤醒线程
  • LockSupport类的park()--unpark()可以阻塞当前线程、以及唤醒指定的被阻塞线程
1. 使用Object中的wait()--notify()方法来阻塞、唤醒线程

wait方法和notify方法两个都不存在于同步代码块时会报错IllegalMonitorStateExxception
结论:

  • Object类中的wait、notify用于线程的等待和唤醒时,都必须在synchronized内部执行(需要用到关键字synchronized)
  • 只有先wait(),后notify()才能被正确唤醒,顺序颠倒则无法唤醒。
2. 使用JUC包中Conditionawait()--signal()方法来阻塞、唤醒线程

与Object类中的wait、notify类似一样有两个约束:

  • 线程先要获得并持有锁,必须在锁块中
  • 必须要先等待,后唤醒,才能被唤醒
3. LockSupport类的park()--unpark()可以阻塞当前线程、以及唤醒指定的被阻塞线程

LockSupport 是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每一个线程都有一个对应的许可(Permit)
Permit只有两个值1和0,其默认是0
可以把许可看成一种(0,1)的信号量(Semaphore),但是与Semaphore不同的是,许可的累加上限为1。

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

  • 无锁块要求
  • 额外支持先唤醒后等待
代码说明
package com.company;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {
    static Object objectLock = new Object();
    public static void main(String[] args) {
        Thread a = new Thread(() -> {
            try{TimeUnit.SECONDS.sleep(2);}catch (InterruptedException e){e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"\t"+"----------conme in");
            LockSupport.park();//等待通知放行,需要许可证
            System.out.println(Thread.currentThread().getName()+"\t"+"----------线程被唤醒");
        }, "A");
        a.start();

        Thread b = new Thread(() -> {
            LockSupport.unpark(a);//为a线程发放了许可证
            System.out.println(Thread.currentThread().getName()+"\t"+"----------通知动作");
        }, "B");
        b.start();
    }
    public static void waitNotify(){
        new Thread(()->{
            synchronized (objectLock){
                try{TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){e.printStackTrace();}
                System.out.println(Thread.currentThread().getName()+"\t"+"----------conme in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+"----------线程被唤醒");
            }
        },"A").start();

        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"\t"+"----------通知动作");
            }
        },"B").start();
    }
}

重点说明
  • LockSupport是一个线程阻塞的工具类,所有方法都是静态方法
  • LockSupport和每一个使用它的线程都有一个许可证相关联,这个许可证是不可以累积的,只有许可和非许可的区别,不会出现可以许可两次这种情况。
  • 底层使用Unsafe类
例题

1. 为什么可以先唤醒后阻塞线程
因为LockSupport和每一个使用它的线程都有一个许可证相关联,使用unpark()唤醒时许可证被被设定为许可,之后调用阻塞park()时可以直接消费这个许可证(重置为0),故同样不会阻塞。
2. 为什么唤醒两次,阻塞两次,仍会有线程被阻塞住
因为LockSupport的许可证是不可以累积的,只有许可和非许可的区别,不会出现可以许可两次的情况。在第一次阻塞时就消费掉了这张许可证,第二次park()时则会阻塞。

你可能感兴趣的:(LockSupport--线程等待唤醒工具类)