【多线程】并发编程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

相关文章

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

MySQL中VARCHAR和TEXT的区别小结

《MySQL中VARCHAR和TEXT的区别小结》MySQL中VARCHAR和TEXT用于存储字符串,VARCHAR可变长度存储在行内,适合短文本;TEXT存储在溢出页,适合大文本,下面就来具体的了解... 目录一、VARCHAR 和 TEXT 基本介绍1. VARCHAR2. TEXT二、VARCHAR

Java JUC并发集合详解之线程安全容器完全攻略

《JavaJUC并发集合详解之线程安全容器完全攻略》Java通过java.util.concurrent(JUC)包提供了一整套线程安全的并发容器,它们不仅是简单的同步包装,更是基于精妙并发算法构建... 目录一、为什么需要JUC并发集合?二、核心并发集合分类与详解三、选型指南:如何选择合适的并发容器?在多

Java 结构化并发Structured Concurrency实践举例

《Java结构化并发StructuredConcurrency实践举例》Java21结构化并发通过作用域和任务句柄统一管理并发生命周期,解决线程泄漏与任务追踪问题,提升代码安全性和可观测性,其核心... 目录一、结构化并发的核心概念与设计目标二、结构化并发的核心组件(一)作用域(Scopes)(二)任务句柄

python中getsizeof和asizeof的区别小结

《python中getsizeof和asizeof的区别小结》本文详细的介绍了getsizeof和asizeof的区别,这两个函数都用于获取对象的内存占用大小,它们来自不同的库,下面就来详细的介绍一下... 目录sys.getsizeof (python 内置)pympler.asizeof.asizeof

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

Go之errors.New和fmt.Errorf 的区别小结

《Go之errors.New和fmt.Errorf的区别小结》本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考... 目录error的基本用法1. 获取错误信息2. 在条件判断中使用基本区别1.函数签名2.使用场景详细对