线程阻塞和唤醒的方法
线程阻塞:某个线程无法继续执行当前任务,而必须等待其他事件发生(如资源释放、信号到达等)才能继续执行的情况
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之前 不然无效
具体如何配合使用都在这里有描述了