线程基础学习

线程的实现

  1. 通过实现Runnable接口的方式,实现其中的run方法。
  2. 继承Thread类,然后重写其中的run方法。
  3. 通过线程池创建线程,默认采用DefaultThreadFactory。
  4. 有返回值的callable,实现callable接口,实行call方法。

本质上,都是通过1,2两种类型创建方式

实现runnable接口要比继承thread类更好

好处:

  1. 可以把不同的内容进行解耦,权责分明。
  2. 某些情况下可以提升性能,减小开销。
  3. 继承Thread类相当于限制了代码未来的可拓展性。

如何正确停止线程

启动线程需要调用Thread类的start()方法,并且在run()方法中定义需要执行的任务。

使用interrupt停止线程,这个是属于通知接口,是否真的暂停取决于线程本身。

public class StopThread implements Runnable {@Overridepublic void run() {int count = 0;// 判断线程释放被中断,并且判断count是否小于1000while (!Thread.currentThread().isInterrupted() && count < 1000) {System.out.println("count = " + count++);// 休眠50秒钟Thread.sleep(50000);}}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new StopThread());thread.start();// 休眠5秒钟Thread.sleep(5000);// 终端线程thread.interrupt();}
}

如果在第一段代码中休眠的时候,第二段代码线程发起中断,则休眠线程则会java.lang.InterruptedException: sleep interrupted 异常中断,抛出异常。

错误的停止方法

  • stop()会把线程停止,导致任务戛然而止有风险
  • suspend()容易导致死锁,因为线程A调用suspend()让B挂起,B线程没有释放锁就进入休眠,如果A想要拿到B持有的锁,这是B在休眠就导致A拿不到锁就死锁了。
  • resume()

使用volatile标记为的停止方法是可能出现问题

public class StopThread implements Runnable {// volatile标记位public volatile boolean canceled = false;@Overridepublic void run() {int count = 0;// 判断线程释放被中断,并且判断count是否小于1000while (!canceled && !Thread.currentThread().isInterrupted() && count < 1000) {System.out.println("count = " + count++);// 休眠50秒钟Thread.sleep(50000);}}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new StopThread());thread.start();// 休眠5秒钟Thread.sleep(5000);// 终端线程thread.interrupt();thread.canceled = true;}
}

线程在6种状态之间转化

线程的生命周期

  1. New(新创建)表示线程被创建但尚未启动的状态,当线程调用start(),就变成Runnable。
  2. Runnable(可运行)表示java中的线程可以running或者是ready状态
  3. Blocked(被阻塞)

从runnable状态转到Blocked状态,只有一种可能性,就是进入synchronized保护的代码时没有抢到monitor锁。

转化成runnable状态:获取到monitor锁

  1. Waiting(等待)

进入waiting状态的三种可能性

a. 没有设置Timeout参数的Object.wait()方法

b. 没有设置Timeout参数的Thread.join()方法

c. LockSupport.park()方法

wait()会释放monitor锁

唤醒:如果其他线程调用notify()或者notifyAll()来唤醒它,会直接进入到Blocked状态。因为唤醒Waiting线程的线程如果调用notify()或者notifyAll(),要求必须首先持有该monitor锁,所以处于waiting状态的线程被唤醒时拿不到该锁,会进入blocked状态

  1. Timed_waiting(计时等待)

跟waiting的区别是时间到了就自动唤醒或者是等待唤醒信号进行唤醒

  1. Terminated(被终止)

wait/notify/notifyAll方法的使用注意事项

为什么wait必须在synchronized保护的同步代码中使用?

/*** 在使用wait()方法时,必须将wait方法写在synchronized保护的while代码块中,并始终判断条件是否满足* 如果满足就往下执行,如果不满足就执行wait()方法,在执行wait()方法之前,必须持有对象的monitor锁,也就synchronized锁*/
public void give(String data) {synchronized (this) {buffer.add(data);notify();}
}public String take() throws InterruptedException {synchronized (this) {while (buffer.isEmpty()) {wait();}return buffer.remove();}
}

为什么wait/notify/notifyAll方法被定义在Object类中,而sleep定义在Thread类中?

因为Java中每个对象都有一把称之为monitor监视器的锁,由于每个对象都可以上锁,这就要求在对象头中有一个用来保存锁信息的位置,这个锁是对象级别的,而非线程级别的,wait/notify/notifyAll也都是锁级别的操作,它们的锁属于对象。

所以把它们定义在Object类中是最合适,因为Object类是所有对象的父类

wait/notify和sleep方法的异同

相同点:

  1. 都可以让线程阻塞
  2. 它们都可以响应interrupt中断:在等待的过程中如果收到中断信号,都可以进行响应,

并抛出InterruptedException异常

不同点:

  1. wait方法必须在synchronized保护的代码中使用,而sleep方法并没有这个要求
  2. 在同步代码中执行sleep方法时,并不会释放monitor锁,但执行wait方法时会主动释放monitor锁
  3. sleep方法中会要求必须定义一个时间,时间到期后会主动恢复,而对于没有参数的wait方法而言,意味着永久等待,直到被中断或被唤醒才能恢复,它并不会主动恢复
  4. wait/notify是Object类的方法,而sleep是Thread类的方法

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

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

相关文章

Linux 用户和用户组管理详解

文章目录 Linux 用户和用户组管理详解用户和用户组概念 添加和删除用户添加用户删除用户 用户组管理添加用户组删除用户组将用户加入用户组 禁用和启用用户临时禁用用户恢复用户 显示用户和组信息高级用户管理批量删除组内用户配置 sudo 权限 用户和用户组的配置文件用户的 UID…

【Linux-基础IO】软硬链接+动静态库

一、软硬链接 见一见 软连接 硬连接 通过观察我们发现以下几点&#xff1a; 1.ll - i后&#xff0c;软连接形成的文件有指向&#xff0c;并且软连接的Inode编号与对应文件的Inode编号不一样 2.ll - i后&#xff0c;硬连接形成的文件与对应的文件Inode编号一样 3.软连接…

大佬,简单解释下“嵌入式软件开发”和“嵌入式硬件开发”的区别

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;首先&#xff0c;嵌入式硬…

初识数据结构--时间复杂度 和 空间复杂度

数据结构前言 数据结构 数据结构是计算机存储、组织数据的方式(指不仅能存储数据&#xff0c;还能够管理数据-->增删改)。指相互之间存在一种或多种特定关系的数据元素的集合。没有单一的数据结构对所有用途都有用&#xff0c;所以我们要学习各种的数据结构&#xff0c;比…

visual studio使用ssh连接linux虚拟机运行程序

1.vs安装linux组件 2.安装后新建项目 新建后会有一个使用指南 设置网络为桥接网卡后打开虚拟机 使用vs提升的那句话安装工具 sudo apt-get install openssh-server g gdb gdbserver 重启ssh服务 sudo service ssh restart 接着进去打开ssh端口 sudo vi /etc/ssh/sshd_config …

5-容器管理工具Docker

├──5-容器管理工具Docker | ├──1-容器管理工具Docker | | ├──1-应用部署容器化演进之路 | | ├──2-容器技术涉及Linux内核关键技术 | | ├──3-Docker生态架构及部署 | | ├──4-使用容器运行Nginx及docker命令介绍 | | ├──5-容器镜像介…

Unity3D 动画回调函数详解

在Unity3D中&#xff0c;动画回调函数是实现精细动画效果的重要工具。通过动画回调函数&#xff0c;我们可以在动画的特定时刻执行自定义代码&#xff0c;从而实现更加灵活和复杂的动画效果。本文将详细解释Unity3D中的动画回调函数&#xff0c;并提供相应的代码实现。 对惹&a…

pytest的基础入门

pytest判断用例的成功或者失败 pytest识别用例失败时会报AssertionError或者xxxError错误&#xff0c;当捕获异常时pytest无法识别到失败的用例 pytest的fixture夹具 pytest的参数化 #coding:utf-8 import pytestfrom PythonProject.pytest_test.funcs.guess_point import ge…

在 MySQL 中通过自定义哈希分片实现大规模数据的多线程并行处理20241008

在 MySQL 中通过自定义哈希分片实现大规模数据的多线程并行处理 虽然 MySQL 没有像 ORA_HASH 这样的内置哈希函数&#xff0c;但我们可以通过其他函数来实现类似的效果&#xff0c;并将数据分片到多个线程中并行处理。下面&#xff0c;我们将使用 MySQL 的 CRC32 函数来演示如…

小程序知识付费的优势 知识付费服务 知识付费平台 知识付费方法

在信息爆炸的时代&#xff0c;知识如同繁星点点&#xff0c;璀璨而散落。如何在这片知识的海洋中精准捕捞&#xff0c;成为现代人追求自我提升的迫切需求。小程序知识付费&#xff0c;正是这样一座桥梁&#xff0c;它以独特的优势&#xff0c;让智慧触手可及&#xff0c;轻触未…

Miniconda更改虚拟环境创建路径

Miniconda更改虚拟环境创建路径 文章目录 Miniconda更改虚拟环境创建路径前言步骤一 修改配置文件步骤二 提升路径访问权限步骤三 测试配置修改是否生效 前言 在我使用 conda 命令创建新的虚拟环境时&#xff0c;发现创建好的虚拟环境存放路径是在 ../miniconda3/pkgs/ 路径下…

Nginx从入门到实战(八):版本平滑无感知,不停机升级

一、查看旧版本信息 可以通过nginx -V命令&#xff0c;来查看当前nginx的版本信息&#xff0c;和配置参数。 [rootnb001 sbin]# nginx -V -bash: nginx: command not found [rootnb001 sbin]# ./nginx -V nginx version: nginx/1.20.1 built by gcc 4.8.5 20150623 (Red Hat …

C/C++语言基础--C++神奇的多态

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 通过前面几节课&#xff0c;我们学习了抽象、封装、继承相关的概念&#xff0c;接下来我们将讲解多态&#xff0c;多态他非常神奇&#xff0c;正式有了他&#xff0c;类才能出现多样性特征&#xff1b;C语言…

【C++差分数组】P1672何时运输的饲料

本文涉及知识点 C差分数组 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 P1672何时运输的饲料 原文比较啰嗦&#xff0c;我简述一下&#xff1a; 第x天运来F1(1<F1<1e6)千克的饲料&#xff0c;第D&#xff08;1<2e3)天还剩F2&…

DBA | 如何将 .mdf 与 .ldf 的数据库文件导入到SQL Server 数据库中?

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 原文链接&#xff1a;DBA | 如何将 .mdf 与 .ldf 的数据库文件导入到SQL Server 数据库中? 如何将 (.mdf) 和 (.ldf) 的SQL Server 数据库文件导入到当前数据库中? Step 1.登录到 Sql Server 服…

【Nginx系列】Nginx启动失败

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Java笔记Day42:初入六六村!(springMVC前后端不同数据类型传值)

1.前端传参方式: 1.url传参&#xff1a; 数据参数直接写在url地址中&#xff0c;一般使用get请求。 使用params传输的就是url类型的参数&#xff1b; &#xff08;params 参数通常用于发送 URL 查询参数。 这些参数会附加到请求的 URL 中&#xff0c; 并以 keyvalue 的形…

数学建模算法与应用 第12章 现代优化算法

目录 12.1 粒子群优化算法 Matlab代码示例&#xff1a;粒子群优化算法求解函数最小值 12.2 遗传算法 Matlab代码示例&#xff1a;遗传算法求解函数最小值 12.3 蚁群算法 Matlab代码示例&#xff1a;蚁群算法求解旅行商问题 12.4 Matlab 遗传算法工具 使用遗传算法工具箱…

docker overlay 占用空间太大,迁移到 /data/

将 Docker 的 overlay 存储驱动迁移到 /data/ 目录下&#xff0c;可以通过以下步骤完成&#xff1a; 1. 停止 Docker 服务 首先&#xff0c;停止 Docker 服务以确保没有容器在运行&#xff0c;并且数据不会被写入到当前的存储位置。 sudo systemctl stop docker2. 备份现有数…

PyCharm打开及配置现有工程(详细图解)

本文详细介绍了如何利用Pycharm打开一个现有的工程&#xff0c;其中包括编译器的配置。 PyCharm打开及配置现有工程 1、打开工程2、配置编译器 1、打开工程 双击PyCharm软件&#xff0c;点击左上角 文件 >> 打开(O)… 选中想要打开的项目之后点击“确定” 2、配置编译器…