线程 阻塞和唤醒的 方法


线程阻塞:某个线程无法继续执行当前任务,而必须等待其他事件发生(如资源释放、信号到达等)才能继续执行的情况

sleep方法

sleep方法在Thread类中 定义也很简单 本地 静态

//指定以毫秒为单位的时间
public static native void sleep(long millis) throws InterruptedException;

凡是native修饰的 说明是本地方法,而本地方法分两类 一个是操作系统控制的,一个是jvm管理的, sleep方法是操作系统控制的,锁是 由监视器锁(Monitor)靠jvm控制管理的,两者没关系,所以sleep不存在释放锁本地方法区分是操作系统还是jvm

该方法作用是让线程睡一会,让出的是CPU时间片,等到时间过去了,线程重新进入可执行状态,给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会

避免在同步块内使用 sleep()

synchronized (lock) {
    
    
    try {
    
    
        Thread.sleep(5000); // 休眠期间不释放锁
        // 其他线程等待锁的时间 = sleep时间 + 业务逻辑时间
    } catch (InterruptedException e) {
    
    
        e.printStackTrace();
    }
}

本质:sleep() 会让当前线程暂停执行,但 不会释放锁,其他需要同一锁的线程会被阻塞,无法执行,若 sleep() 时间过长,可能导致系统整体吞吐量下降,甚至因资源竞争引发死锁

wait系列方法和notify() / notifyAll()方法

wait系列方法是Object类中的方法。Objec是java中的顶级父类,在jvm中管理的

    public final void wait() throws InterruptedException {
    
    
        wait(0);
    }
    public final native void wait(long timeout) throws InterruptedException;

wait方法是让调用者线程释放对象的锁并让调用者线程等待,核心点让调用者线程释放对象锁让调用者线程等待,wait()会让调用者线程先释放锁住的对象,然后让调用者线程再执行等待的动作,让其他线程可以获得锁取去执行,
wait()方法是默认wait(0)除非唤醒一直等待状态,
而wait(>0)则是到时间了 去排队获取锁然后重新开始执行

注意事项是
wait() 必须在 synchronized 块中调用,否则抛出 IllegalMonitorStateException,因为wait方法作用是让调用者线程释放对象的锁,释放锁的前提是先拿到锁,拿锁的前提是有锁,synchronized 作用就是加锁的

wait()使让调用者线程进入阻塞状态,两个方法就是唤醒线程,专门配合wait方法使用的,区别在于
notify()精准唤醒某个wait()的线程,notifyAll()唤醒全部wait()的线程

    public final native void notify();
    public final native void notifyAll();

join系列方法

join系列方法底层核心还是wait方法,唤醒方式底层的notify() / notifyAll()关于Thread类和jion源码介绍链接

Join的使用认知误区
总以为thread.join() 就是让线程thread等待一会,比如下面这段代码,两个线程 主线程Main方法 子线程childThread,而使用childThread.join()就是让子线程阻塞等待,这是错误认为的

public static void main(String[] args) throws InterruptedException {
    
    
    Thread childThread = new Thread(() -> {
    
    
        try {
    
    
            Thread.sleep(2000); // 子线程休眠2秒
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    });
	childThread.start();
    childThread.join(); 
}

我们可以加点打印顺序,在join方法调用前后和子线程调用sleep前加点打印内容,看下执行顺序,会发现:子线程除了sleep之外并没有一直阻塞等待 接着才是主线程main执行

原因就出在join的方法修饰synchronized和底层实现上,简化join代码方法

public final synchronized void join(long millis) throws InterruptedException {
    
    
	.............................
        while (isAlive()) {
    
     
            wait(0); 
        }
       ......................... 
}

上面说了wait(0)方法必须要和synchronized关联使用的,否则会报错的,而wait方法作用是当前线程释放锁,使当前线程进入阻塞等待状态
join方法是同步方法(synchronized),synchronized的作用synchronized介绍链接,synchronized修饰的实例方法,作用给当前对象实例加锁,当前对象实例是childThread,那就是给childThread加锁,上面示例中当前线程是主线程,因为是从主线程中执行的childThread.join(); 所以当前线程还是主线程,wait方法作用是 wait(0)让调用者线程(主线程)释放childThread对象的锁并等待,而非让childThread线程等待

看过很多介绍都说谁调用 谁等待,更全面的说法是哪个调用者线程调用 哪个调用者线程等待,毕竟wait是让线程停下等待 而不是让实例对象停下等待

yield()

方法定义就是一个静态本地方法:暂停当前正在执行的线程对象,并执行其他线程。意义不太大

    public static native void yield();
  • yield()方法是jvm控制:用于提醒调度器当前线程愿意放弃CPU资源,让其他具有相同或更高优先级的线程有机会执行

suspend() 和 resume() 方法

挂起和唤醒线程,suspend e()使线程进入阻塞状态,只有对应的resume e()被调用的时候,线程才会进入可执行状态。(不建议用,容易发生死锁)

park和unpark方法

这个方法在AQS系列里面常见,作用是挂起线程,挂起的线程就不在执行 等待被解挂unpark或者中断退出park

LockSupport.park(Thread.currentThread());

像这样就意味这当前线程被挂起不在执行,执行代码就停留在park这一行,直到解挂 恢复从park处再开始执行代码,unpark方法就是配合park使用的

LockSupport.unpark(s.thread);

中断Interrupt

中断好理解,某种操作被停止就是打断,比如 去吃饭 被打断 不吃了,那去睡觉被 打断 不睡了
Interrupt 是 thread类中的一个方法 Thread介绍链接

注意事项就是中断要设置在 比如sleep wait park之前 不然无效
具体如何配合使用都在这里有描述了

猜你喜欢

转载自blog.csdn.net/weixin_44891364/article/details/146494831
今日推荐