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

相关文章

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.使用场景详细对

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

AOP编程的基本概念与idea编辑器的配合体验过程

《AOP编程的基本概念与idea编辑器的配合体验过程》文章简要介绍了AOP基础概念,包括Before/Around通知、PointCut切入点、Advice通知体、JoinPoint连接点等,说明它们... 目录BeforeAroundAdvise — 通知PointCut — 切入点Acpect — 切面

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess