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

相关文章

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

高并发环境中保持幂等性

在高并发环境中保持幂等性是一项重要的挑战。幂等性指的是无论操作执行多少次,其效果都是相同的。确保操作的幂等性可以避免重复执行带来的副作用。以下是一些保持幂等性的常用方法: 唯一标识符: 请求唯一标识:在每次请求中引入唯一标识符(如 UUID 或者生成的唯一 ID),在处理请求时,系统可以检查这个标识符是否已经处理过,如果是,则忽略重复请求。幂等键(Idempotency Key):客户端在每次

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使