java基础系列(六) sleep()和wait() 区别

news2024/11/26 18:49:17

一.前言

        关于并发编程这块, 线程的一些基础知识我们得搞明白, 本篇文章来说一下这两个方法的区别,对Android中的HandlerThread机制原理可以有更深的理解, HandlerThread源码理解,请查看笔者的这篇博客:

HandlerThread源码理解_handlerthread 源码_broadview_java的博客-CSDN博客

二.区别

2.1 sleep方法

public static native void sleep(long millis)方法是Thread的静态方法,很显然它是让当前线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。需要注意的是如果当前线程获得了锁,sleep方法并不会失去锁

Thread类中的静态方法sleep(),当一个执行中的线程调用了Thread的sleep()方法后,调用线程会暂时让出时间的执行权,这期间不参与cpu的调度,但是该线程持有的锁是不让出的。时间到了会正常返回,线程处于就绪状态,然后参与cpu调度,获取到cpu资源之后就可以运行。

我们通过这个Demo来看下

public class SleepTest {

    private static Object lock = new Object();

    public static void main(String[] args) {
        new Thread(()->{

            synchronized (lock) {
                System.out.println("线程A休眠10秒不放弃锁");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("A休眠10秒醒来");
            }
        }).start();
        
        new Thread(()->{
            synchronized (lock){
                System.out.println("线程B休眠5秒不放弃锁");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("B休眠5秒醒来");
            }
        }).start();
    }
}

打印结果如下:

线程A休眠10秒不放弃锁
A休眠10秒醒来
线程B休眠5秒不放弃锁
B休眠5秒醒来

线程B休眠5秒不放弃锁
B休眠5秒醒来
线程A休眠10秒不放弃锁
A休眠10秒醒来

解释如下:

无论执行多少次,都是先A输出再B输出 或者 先B输出再A输出,不会出现交叉输出的情况,
因为A获取到锁之后,即使是sleep也不会释放锁,因B获取不到锁,也就无法执行。

总结:  sleep方法不会释放锁

2.2 wait方法

Object.wait方法会释放锁对象

一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的 Object.notify()方法 或 Object.notifyAll()方法。

其实waiting状态并不是一个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程之间的协作关系, 也是java并发编程要处理的核心问题.

当多个线程协作时,比如A,B线程,如果A线程在Runnable(可运行)状态中调用了wait()方法那么A线程就进入了Waiting(无限等待)状态,同时失去了同步锁。

假如这个时候B线程获取到了同步锁,在运行状态中调用了notify()方法,那么就会将无限等待的A线程唤醒。

注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)。
 

我们来看这个Demo :

public class wakeLockTest {

    private static Object lock = new Object(); 

    public static void main(String[] args) {


        Thread thread1 = new Thread(new Runnable1(), "线程1");
        thread1.start();


        Thread thread2 = new Thread(new Runnable2(), "线程2");
        thread2.start();
    }

    static class Runnable1 implements Runnable{
        @Override
        public void run() {
            synchronized (lock) {
                try {
                    lock.wait();
                    System.out.println("当前线程名: "+Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Runnable2 implements Runnable{
        @Override
        public void run() {
            synchronized (lock) {
                lock.notify();
                System.out.println("当前线程名: "+Thread.currentThread().getName());
            }
        }
    }
}

如果不加同步锁相关的代码,   从代码流程来看, 线程1 先执行, 线程2再执行,肯定会打印

当前线程名: 线程1
当前线程名: 线程2

加入锁相关的代码后,打印如下:

三. 总结


sleep()方法是Thread类的方法,线程通过调用该方法,进入休眠状态主动让出CPU,从而CPU可以执行其他的线程。经过sleep指定的时间后,CPU回到这个线程上继续往下执行。

如何当前线程进入了同步锁,sleep()方法并不会释放锁。即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。

使用场合:
线程的调度执行是按照其优先级的高低顺序进行的,当高级别的线程未死亡时,低级别的线程没有机会获得CPU资源。有时优先级高的线程需要优先级低的线程完成一些辅助工作或者优先级高的线程需要完成一些比较费时的工作,此时优先级高的线程应该让出CPU资源,使得优先级低的线程有机会执行。为了达到这个目的,优先级高的线程可以在自己的run()方法中调用sleep方法来使自己放弃CPU资源,休眠一段时间。

wait()方法

Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内

从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。

相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束后,才会释放锁.
 

两者区别
1.sleep()方法,属于Thread类的。; wait()方法,则是属于Object类的;
2.sleep方法不会释放锁,而wait方法会释放锁
3.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4.sleep可以自然醒,wait必须等待被唤醒.


 


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/373185.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

requests库---(1)requests简介

目录:导读 request简介 requests安装 requests发送get请求 requests请求post 返回值其他内容 写在最后 在做接口测试,接口自动化测试的时候都会用到很多工具,如postman、jmeter、pytest等工具,除了这些工具外,我…

ESP32设备驱动-MAX30100心率监测传感器驱动

MAX30100心率监测传感器驱动 1、MAX30100介绍 MAX30100 是一款集成脉搏血氧饱和度和心率监测传感器解决方案。 它结合了两个 LED、一个光电探测器、优化的光学器件和低噪声模拟信号处理,以检测脉搏血氧饱和度和心率信号。 MAX30100 采用 1.8V 和 3.3V 电源供电,可通过软件…

单机模拟kafka分布式集群(演示生产、消费数据过程)

用单机搭建kafka伪分布式集群,其实集群的概念并不复杂 先说明一下,以下的每个服务启动后都需要新开一个终端来启动另外的服务(因为是集群,自然会用多个终端) 首先下载kafka 提取码:dvz4 或者直接去官网下载kafka_2.11-1.0.0.tgz t…

DevOps实战50讲-(1)彻底理解DevOps

持续坚持原创输出,点击蓝字关注我吧软件质量保障:所寫即所思|一个阿里质量人对测试的所感所悟。浅谈软件开发流程软件开发流程是从需求分析、设计、编码、测试到上线等一系列环节的步骤和活动。通常来说,软件开发流程可以分为以下几个阶段&am…

Vue3电商项目实战-商品详情模块7【21-商品详情-评价组件-头部渲染、22-商品详情-评价组件-实现列表】

文章目录21-商品详情-评价组件-头部渲染22-商品详情-评价组件-实现列表21-商品详情-评价组件-头部渲染 目的:根据后台返回的评价信息渲染评价头部内容。 yapi 平台可提供模拟接口,当后台接口未开发完毕或者没有数据的情况下,可以支持前端的开…

CentOS 7安装Docker并使用tomcat测试

文章目录环境准备Docker安装安装tomcat环境准备 CentOS 7以上版本linux内核版本需要在3.10以上,可通过uname -r 查看系统内核。 Docker安装 检查docker安装源 yum list docker yum安装docker : yum install docker.x86_64 启动 docker : s…

操作系统权限提升(十六)之绕过UAC提权-CVE-2019-1388 UAC提权

系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权 操作系统权限提升(十四)之绕过UAC提权-基于白名单AutoElevate绕过UAC提权 操作系统权限提升(十五)之绕过UAC提权-基于白名单DLL劫持绕过UAC提权 注&a…

QML Item和Rectangle详解

1.Item和Rectangle Item类型是Qt Quick中所有可视项的基本类型。 Qt Quick中的所有可视项都继承Item。尽管Item对象没有视觉外观,但它定义了视觉项中常见的所有属性,例如x和y位置、宽度和高度、锚定和键处理支持。 Rectangle继承自Item,多…

数组初始化方式与decimal.InvalidOperation

数组初始化方式与decimal.InvalidOperation调用函数主函数: 数组声明不同带来的报错与否1. 报错decimal.InvalidOperation的数组初始化版本2. 可行的初始化版本输出结果1. 报错时的内容2. 正常的输出计算结果原因(是否是数组与列表不同引起(?…

因果推断10--一种大规模预算约束因果森林算法(LBCF)

论文:A large Budget-Constrained Causal Forest Algorithm 论文:http://export.arxiv.org/pdf/2201.12585v2.pdf 目录 0 摘要 1 介绍 2 问题的制定 3策略评价 4 方法 4.1现有方法的局限性。 4.2提出的LBCF算法 5验证 5.1合成数据 5.2离线生…

gitlab部署使用,jenkins部署使用

gitlab部署使用,jenkins部署使用gitlab下载gitlab安装gitlab使用gitlab设置中文修改管理员密码创建组,创建项目,创建用户jenkins下载jenkins安装jenkin使用jenkins更改管理员密码配置拉取代码配置登录gitlab拉取代码的账号密码配置项目配置gitlab仓库配置构建构建构…

动态分区分配计算

动态分区分配 内存连续分配管理分为: 单一连续分配固定分区分配动态分区分配(本篇所讲) 首次适应算法(First Fit,FF) 该算法又称最先适应算法,要求空闲分区按照首地址递增的顺序排列。 优点…

数据结构——树

深度优先/广度优先遍历深度优先:访问根节点对根节点的 children 挨个进行深度优先遍历const tree {val: "a",children: [{val: "b",children: [{val: "d",children: [],},{val: "e",children: [],},],},{val: "c&quo…

可靠性设计

目录 一、可靠性设计概述 二、冗余的类型 三、冗余系统的设计 1.N版本程序设计 2.恢复块设计 3.防卫式程序设计 4.双机容错 一、可靠性设计概述 可靠性指系统能够正常运行的概率。如何设计出一个具有高可靠性的系统呢?可以利用避错技术,容错技术…

【LeetCode】剑指 Offer 16. 数值的整数次方 p110 -- Java Version

题目链接:https://leetcode.cn/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/ 1. 题目介绍(16. 数值的整数次方) 实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,…

文献计量分析方法:Citespace安装教程

Citespace是一款由陈超美教授开发的可用于海量文献可视化分析的软件,可对Web of Science,Scopus,Pubmed,CNKI等数据库的海量文献进行主题、关键词,作者单位、合作网络,期刊、发表时间,文献被引等…

数据结构入门5-2(数和二叉树)

目录 注: 树的存储结构 1. 双亲表示法 2. 孩子表示法 3. 重要:孩子兄弟法(二叉树表示法) 森林与二叉树的转换 树和森林的遍历 1. 树的遍历 2. 森林的遍历 哈夫曼树及其应用 基本概念 哈夫曼树的构造算法 1. 构造过程 …

响应性基础API

一.什么是proxy和懒代理?什么是proxy?proxy对象是用于定义基本操作的自定义行为(如:属性查找,赋值,枚举,函数调用等等)。什么是懒代理?懒代理:在初始化的时候不会进行全部代理,而是…

数据仓库Hive

HIve介绍 Hive是建立在Hadoop上的数据仓库基础构架。它提供了一系列的工具,可以用来进行数据提取转化加载,可以简称为ETL。 Hive 定义了简单的类SQL查询语言,称为HQL,它允许熟悉SQL的用户直接查询Hadoop中的数据&#xf…

三万字全面概述关于5G-V2X技术和应用

5G技术有望实现更快的网联链接、更低的延迟、更高的可靠性、更大的容量和更广的覆盖范围。希望依靠这些技术来实现车辆到一切(V2X)的通信,除了道路安全外,还能提高车辆的安全性和自动驾驶性能,节约能源和成本。车辆通信…