【多线程】并发编程wait和sleep的区别

2024-09-02 22:44

本文主要是介绍【多线程】并发编程wait和sleep的区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

notyfy、notifyAll、wait的使用:sleep/wait/notify/notifyAll分别有什么作用

背景:之前的博客讲解到了notify的使用,那并发编程的时候,到底该用 sleep还是notify呢?本篇我们来一起梳理一下区别

在这里插入图片描述

  1. 所属类与方法类型
  • wait():wait方法是Object类的一个方法。它用于线程间的通信,允许一个线程等待另一个线程的通知(通过notify()或notifyAll()方法)来继续执行。
  • sleep():sleep是Thread类的一个静态方法。它用于暂停当前线程的执行一段时间,但不会释放对象锁。
  1. 锁释放
    wait():调用wait()方法的线程会释放它持有的对象锁,并进入等待状态,直到其他线程调用该对象的notify()或notifyAll()方法来唤醒它。
    sleep():调用sleep()方法的线程不会释放它持有的任何锁。它只是在当前线程的执行路径上暂停一段时间。

  2. 使用场景
    wait():wait通常用于线程间的交互和通信,特别是在需要等待某个条件成立时。它要求调用线程必须拥有对象的锁。
    sleep():sleep常用于暂停线程执行一段时间,例如,模拟延时、减少CPU占用等。它不要求调用线程拥有任何对象的锁。

  3. 唤醒机制
    wait():wait()方法被调用后,可以通过其他线程调用该对象的notify()或notifyAll()方法来唤醒。
    sleep():sleep()方法睡眠指定时间之后,线程会自动苏醒,或者通过调用interrupt()方法提前打断睡眠。

  4. 调用位置
    wait():wait()方法必须在同步方法或同步代码块中调用,因为它要求调用线程必须拥有对象的锁。否则,会抛出IllegalMonitorStateException异常。
    sleep():sleep()方法可以在任何地方调用,因为它不涉及对象锁的释放和获取。

  5. 返回值与异常
    wait():wait()方法不返回任何值,并且在等待过程中可能会抛出InterruptedException异常。
    sleep():sleep(long millis)方法也不返回任何值,但在等待过程中同样可能会抛出InterruptedException异常。

总结

sleep和wait都可以让线程阻塞,也都可以指定超时时间,甚至还都会抛出中断异常InterruptedException。
而它们最大的区别就在于,sleep时线程依然持有锁,别人无法进当前同步方法;wait时放弃了持有的锁,其它线程有机会进入该同步方法。多次提到同步方法,因为wait必须在synchronized同步代码块中,否则会抛出异常IllegalMonitorStateException,notify也是如此,可以说wait和notify是就是为了在同步代码中做线程调度而生的

简单的例子展现sleep和wait的区别

package com.atguigu.signcenter.thread;import lombok.extern.slf4j.Slf4j;import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;/**** sleep和wait的区别* @author: jd* @create: 2024-09-02*/
@Slf4j
public class ThreadSleepAndWait {// 日志行号记录private AtomicInteger count = new AtomicInteger();public static void main(String[] args) throws InterruptedException {ThreadSleepAndWait threadSleepAndWait = new ThreadSleepAndWait();Thread t1 = new Thread(()->{try {threadSleepAndWait.test();} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 =new Thread(()->{try {threadSleepAndWait.test();} catch (InterruptedException e) {e.printStackTrace();}});t1.start();t2.start();}/*** sleep  和 notify的区别*/private synchronized void test() throws InterruptedException {log("进入了同步方法,并开始睡觉sleep,3s");// sleep不会释放锁,因此其他线程不能进入这个test()方法,所以另外一个线程不会输出“进入了同步方法,并开始睡觉 这个提示”Thread.sleep(3000);log("sleep完成,释放掉锁,接着wait等待5s");//wait阻塞在此,因为他会释放锁,其它线程可以进入这个方法//当其它线程调用此对象的notify或者notifyAll时才有机会停止阻塞//就算没有人notify,如果超时了也会停止阻塞//在下面这行代码执行之后,另外一个线程才会进来test()方法log("wait(5000)等待执行之前,这里当前执行的线程释放掉锁,所以另外一个线程就能进行进入这个方法开始执行了" +",同时新线程开始wait(5000)");wait(2000);log("wait(5000)等待执行之后"); //这里thread0释放了锁并开始等待,所以Thread-1抢到了执行权,就开始打印最开始的提示log("我要走了,但我要再睡一觉sleep(10000),10s");//这里睡的时间很长,因为没有释放锁,其它线程就算wait超时了也无法继续执行Thread.sleep(10000);log("走了,调用notify方法,停止对当前同一个threadSleepAndWait对象的wait阻塞,对他进行唤醒");
//        notify();}// 打印日志private void log(String s) {System.out.println(count.incrementAndGet() + " "+ new Date().toString().split(" ")[3]+ "\t" + Thread.currentThread().getName() + " " + s);}
}

结果(无解析):

1 17:04:20	Thread-0 进入了同步方法,并开始睡觉sleep,3s
2 17:04:23	Thread-0 sleep完成,释放掉锁,接着wait等待5s
3 17:04:23	Thread-0 wait(5000)等待执行之前,这里当前执行的线程释放掉锁,所以另外一个线程就能进行进入这个方法开始执行了,同时新线程开始wait(5000)
4 17:04:23	Thread-1 进入了同步方法,并开始睡觉sleep,3s
5 17:04:26	Thread-1 sleep完成,释放掉锁,接着wait等待5s
6 17:04:26	Thread-1 wait(5000)等待执行之前,这里当前执行的线程释放掉锁,所以另外一个线程就能进行进入这个方法开始执行了,同时新线程开始wait(5000)
7 17:04:28	Thread-0 wait(5000)等待执行之后
8 17:04:28	Thread-0 我要走了,但我要再睡一觉sleep(10000),10s
9 17:04:38	Thread-0 走了,调用notify方法,停止对当前同一个threadSleepAndWait对象的wait阻塞,对他进行唤醒
10 17:04:38	Thread-1 wait(5000)等待执行之后
11 17:04:38	Thread-1 我要走了,但我要再睡一觉sleep(10000),10s
12 17:04:48	Thread-1 走了,调用notify方法,停止对当前同一个threadSleepAndWait对象的wait阻塞,对他进行唤醒

结果解析:


1 17:04:20	Thread-0 进入了同步方法,并开始睡觉sleep,3s   // Thread-0首先进入同步方法,Thread-1只能门外候着
2 17:04:23	Thread-0 sleep完成,释放掉锁,接着wait等待5s   //Thread-0 sleep 1秒这段时间,Thread-1没进来,证明sleep没有释放锁
3 17:04:23	Thread-0 wait(5000)等待执行之前,这里当前执行的线程释放掉锁,所以另外一个线程就能进行进入这个方法开始执行了,同时新线程开始wait(5000)  // Thread-0开始wait后Thread-1马上就进来了,证明线程的wait释放了锁 
4 17:04:23	Thread-1 进入了同步方法,并开始睡觉sleep,3s  //因为Thread-0开始wait了,释放锁了,所以Thread-1就拿到锁开始进入test方法
5 17:04:26	Thread-1 sleep完成,释放掉锁,接着wait等待5s  //执行到sleep,Thread-1一直持有锁,
6 17:04:26	Thread-1 wait(5000)等待执行之前,这里当前执行的线程释放掉锁,所以另外一个线程就能进行进入这个方法开始执行了,同时新线程开始wait(5000) //Thread-1 并睡醒之后开始wait,Thread-1也释放了锁,
7 17:04:28	Thread-0 wait(5000)等待执行之后 //Thread-0 wait结束了,所以Thread-0开始往下走,走到下面的sleep继续睡觉
8 17:04:28	Thread-0 我要走了,但我要再睡一觉sleep(10000),10s  //Thread-0开始sleep,并一直拿着锁,
9 17:04:38	Thread-0 真的走了,停止对当前同一个threadSleepAndWait对象的wait阻塞 //释放锁,并输出此行内容
10 17:04:38	Thread-1 wait(5000)等待执行之后  //此时Thread-1开始拿到锁开始下面的睡觉,到最后执行完毕
11 17:04:38	Thread-1 我要走了,但我要再睡一觉sleep(10000),10s
12 17:04:48	Thread-1 真的走了,停止对当前同一个threadSleepAndWait对象的wait阻塞

这篇关于【多线程】并发编程wait和sleep的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131278

相关文章

RabbitMQ消费端单线程与多线程案例讲解

《RabbitMQ消费端单线程与多线程案例讲解》文章解析RabbitMQ消费端单线程与多线程处理机制,说明concurrency控制消费者数量,max-concurrency控制最大线程数,prefe... 目录 一、基础概念详细解释:举个例子:✅ 单消费者 + 单线程消费❌ 单消费者 + 多线程消费❌ 多

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL