JAVA----Thread

Thread

这里写目录标题

  • Thread
    • 线程
      • Thread 第 1 种写法
        • 此外, t.start()的作用
      • Thread 第 2 种写法
      • Thread 第 3 种写法
      • Thread 第 4 种写法
      • Thread 第 5 种写法

线程

本身是操作系统提供的, 操作系统提供了 API 以让我们操作线程, JVM 就对操作系统 API 进行了封装.
线程这里, 则提供了 Thread 类, 表示线程. 利用类中的创建的实例, 以及实例里面创建的一系类方法, 我们就可以完成多线程编程了.

Thread 第 1 种写法

代码示例

package thread;
class MyThread extends Thread{//注解: 相当于是 提示编译器, 进行更严格的检查@Overridepublic void run() {//run(): 描述线程的工作System.out.println("hello thread");}
}public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();}
}

结果

hello threadProcess finished with exit code 0//进程结束

上述代码中, 有 2 个线程:

  1. t 线程;
  2. main 方法所在的线程 (主线程).

但以上执行, 无法很直观的看见两个线程的执行, 我们再对其进行修改.
代码如下

package thread;
class MyThread extends Thread{//注解: 相当于是 提示编译器, 进行更严格的检查@Overridepublic void run() {//run(): 描述线程的工作while(true){System.out.println("hello thread");}}
}public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();while(true){System.out.println("hello main");}}
}

运行结果是两者交替 并发 执行.

我们可以使用 jdk 中包含的 jconsole 工具来观察线程.

在这里插入图片描述
thread.Demo1则是我们创建的线程, 选中连接.

在这里插入图片描述
再点击 线程

在这里插入图片描述
我们可以看到, 执行一个文件, 有 15 个线程, 并且在下方能看见 main 和 Thread-0 两个线程.

我们分别点击main 与 Thread线程
在这里插入图片描述
在这里插入图片描述
如图, 我们可以看到线程的调用栈, main 在调用sleep() 方法, Thread 有一个run() 方法在调用sleep().

我们同样可以打开任务管理器

在这里插入图片描述

我们可以看见这一个 循环, 吃了很多的CPU资源

故而我们利用 sleep() 方法, 来让线程主动进入"阻塞状态", 主动离开 CPU, 睡眠 / 休眠 一段时间.

try {Thread.sleep(1000);//休眠1秒(单位 ms)
} catch (InterruptedException e) {throw new RuntimeException(e);
}

等时间到了之后, 线程就会解除 “阻塞状态”, 重新被调度到 CPU 上执行.

package thread;
class MyThread extends Thread{//注解: 相当于是 提示编译器, 进行更严格的检查@Overridepublic void run() {//run(): 描述线程的工作while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread t = new MyThread();t.start();while(true){System.out.println("hello main");try{Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}

在这里插入图片描述
此时的 CPU 占用率大幅降低.

此时涉及到一个问题
在未来实际开发中, 发现服务器程序, 消耗 CPU 资源超出预期, 如何排查这个问题.

则 先需要确认哪个线程消耗的 CPU 比较高: 利用第三方工具找到此线程, 确定以后, 进一步 排查 线程中是否有类似 “非常快速” 的循环.
然后确认 此循环是否应该这么快, 若应该, 则升级 CPU. 若不应该, 则需要在循环中引入 等待操作(不一定是sleep() ).

然而
每个线程打印可能是 main 在前, 也可能是 thread 在前, 多个线程的调度是无序的, 在操作系统内部也称为 “抢占式执行”. 充满了随机性, 正是如此, 使用多线程是难以预测的.

此外, t.start()的作用
package thread;
class MyThread extends Thread{//注解: 相当于是 提示编译器, 进行更严格的检查@Overridepublic void run() {//run(): 描述线程的工作while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread t = new MyThread();
//      t.start();t.run();//这里则是没有创建线程, 就是在主线程中执行上述 run 中的循环打印.while(true){System.out.println("hello main");try{Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}

t.start() (Thread类中自带的方法):
调用 操作系统 提供的"创建线程" api,
在内核创建对应的 pcb 加入到链表中,
进一步的系统调度到这个线程了之后,
就会执行上述 run 方法中的逻辑.

当我们运行时, 只能看见起循环 Thread线程 内的打印操作, 而看不见 main线程 的打印操作.
此时的 run 和 主线程的循环是 串行执行, 不是 “并发执行”. 必须要求 run 中的循环结束, 才能继续执行到下一个循环.

像以上的 run, 定义好,而不去手动调用, 把这个方法交给系统/其他的库/其他框架调用.这样的方法称为: “回调函数”(callback function).

java数据结构, 谈到了比较强, 类似于 回调函数 的效果. PriorityQueue 优先级队列.

多线程 run 方法, 和上述两个情况本质一样, 都是回调函数.

Thread 第 2 种写法

package thread;
//Runnable: 描述一个任务
class MyRunnable implements Runnable{@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Demo2 {public static void main(String[] args){Thread t = new Thread(new MyRunnable());t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

第一种写法, 是 Thread 自己记录自己的操作.
第二种写法, 是 Runnable 记录操作, Thread 负责执行.

当运行内容 与 执行 两个操作分离开, 实现了解耦合操作.
在java中 解耦合 使用接口实现.

解耦合的作用: 简单来讲, 就是使代码的维护难度降低.

Thread 第 3 种写法

package thread;public class Demo3 {public static void main(String[] args) {Thread t = new Thread(){/*这里的 new Thread 并非是 new 一个 Thread, 而是几个操作融合在了一起1. 创建了一个 Thread的子类(匿名的)2. 同时创建了一个该子类id实例, 对于匿名内部类只能创建这一个实例,这个实例创建完之后, 再也拿不到这个匿名内部类了.3. 此处的子类内部重写了父类 run 方法.*/@Overridepublic void run() {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};t.start();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

Thread 第 4 种写法

package thread;public class Demo4 {public static void main(String[] args) {//Runnable 匿名内部类,//此处匿名内部类,只针对 Rnunable,与 Thread 无关//把 Runnable 实例, 作为参数传入到 Thread 的构造方法内Thread t = new Thread(new Runnable() {/*1.创建新的类, 实现Runnable.但类名是匿名的.2.同时创建了这个新类的实例(一次性)3.重写run方法*/@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}

Thread 第 5 种写法

lambda 表达式

package thread;
//使用 lambda 表达式
public class Demo5 {public static void main(String[] args) {//此处的 lambda 就是要代替Demo4重写的 run方法Thread t = new Thread(() -> {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

以上 5 种 方法,本质上都是
1.要把线程执行的任务内容表达出来.
2.通过 Thread 的 start 来创建/启动系统种的线程.
(Thread 对象和操作系统内核中的线程是 一 一 对 应 的关系)

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

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

相关文章

PVE虚拟机隐藏状态栏虚拟设备

虚拟机启动后,状态栏会出现一些虚拟设备,点击弹出会导致虚拟机无法使用。 解决方案: 1、在桌面新建disable_virtio_removale.bat文件,内容如下: ECHO OFF FOR /f %%A IN (reg query "HKLM\SYSTEM\CurrentContro…

【注释和反射】类加载的过程

继上一篇博客【注释和反射】获取class类实例的方法-CSDN博客 目录 三、类加载的过程 例子 三、类加载的过程 在Java虚拟机(JVM)中,类加载是一个将类的字节码文件从文件系统或其他来源加载到JVM的内存中,并将其转换为类或接口的…

年如何在不丢失数据的情况下解锁锁定的 Android 手机?

当您忘记密码、PIN 码或图案并且想要解锁 Android 手机时,您可能会丢失 Android 手机上的数据。但您无需再担心,因为在这里,我们想出了几种解锁锁定的 Android 手机而不丢失数据的方法。 方法 1. 使用 Android Unlock 解锁锁定的 Android 且不…

mathtype设置公式编号,公式居中以及编号靠右

在word中实现: 1. 首先点击栏,选择更多栏去看 看到栏的宽度,然后去设置样式 在开始-样式中设置,新建样式: 新建样式,然后设置格式-制表位,选择对齐方式,居中对齐设置刚才的一半,右…

RK3568 学习笔记 : u-boot 千兆网络功能验证

前言 开发板型号: 【正点原子】 的 RK3568 开发板 使用 虚拟机 ubuntu 20.04 编译 RK3568 Linux SDK,生成镜像,烧写后,Linux 系统正常启动 开启后可以使用 CTRLC 进入 u-boot 本篇验证一下 u-boot 下网络功能 【正点原子】 rk…

MATLAB 运算符

MATLAB 运算符 运算符是一个符号,告诉编译器执行特定的数学或逻辑操作。MATLAB设计为主要在整个矩阵和数组上运行。因此,MATLAB中的运算符既可以处理标量数据,也可以处理非标量数据。MATLAB允许以下类型的基本运算- 算术运算符 关系运算符…

Windows SMBGhost CVE-2020-0796 Elevate Privileges

SMBGhost CVE-2020-0796 Microsoft Windows 10 (1903/1909) - ‘SMBGhost’ SMB3.1.1 ‘SMB2_COMPRESSION_CAPABILITIES’ Local Privilege Escalation https://www.exploit-db.com/exploits/48267 Github https://github.com/danigargu/CVE-2020-0796 修改载荷[可选] 生成 c# …

【JAVA基础之IO】字节流、字符流以及乱码问题

🔥作者主页:小林同学的学习笔录 🔥mysql专栏:小林同学的专栏 目录 1. IO概述 1.1 什么是IO 1.2 IO的分类 1.3 字节和字符流的顶级父类 2. 字节流 2.1 一切皆为字节 2.2 字节输出流【OutputStream】 2.3 FileOutputStream类…

【GUI软件】小红书关键词搜索结果批量采集

一、背景介绍 1.1 爬取目标 用python开发的爬虫采集软件,可自动按关键词抓取小红书笔记。 软件界面截图: 爬取结果截图: 结果截图1: 结果截图2: 结果截图3: 以上。 1.2 演示视频 软件运行演示: 【软件演示】小红书搜…

零信任网络安全技术雷达图3.0正式发布

伴随零信任理念的发展和实施,零信任技术体系的演进变革也日趋完善。易安联力图通过持续更新并优化零信任雷达图,以反映零信任技术在应对新型网络安全挑战中的创新和发展,为企业和组织提供前瞻性的技术建议,助力其构建更为强大和灵…

11.泛型

文章目录 1 泛型概念2. 自定义泛型结构3 泛型方法4 泛型在继承上的体现5 通配符的使用 1 泛型概念 所谓泛型就是用标识符标识不确定的类型,详细说就是:定义类或接口时用标识符表示类中某个属性的类型或者是某个方法的返回值及参数类型。泛型将在使用时&a…

探索React Router:实现动态二级路由

我有一个路由配置的二维数组,想根据这个数组结合路由组件来动态生成路由,应该怎么样实现。在 React Router 6 中渲染二级路由的方式跟 React Router 65相比有一些变化,但核心思路仍然是利用 Route 组件和路由嵌套的方式。下面是具体的步骤: 定义路由数组…

小程序视频如何下载到手机#下载高手

在本文中,我将向您介绍一个工具:下载高手,帮助您轻松下载小程序视频,并将其保存到您的手机中。无论是学习教育类的小程序视频,还是图片素材类的小程序,这些方法都适用于各种类型的小程序。 工具我已经打包好了&#x…

蓝桥杯 — — 完全日期

完全日期 友情链接:完全日期 题目: 思路: 直接从20010101枚举到20211231,然后再判断每一个数是否是一个合法的日期,如果这个日期是合法的,接着判断这个日期的每一个位置上的数字之和是否是一个完全平方数…

GPT-SoVITS声音克隆训练和推理(新手教程,附整合包)

环境: Win10 专业版 GPT-SoVITS-0421 整合包 问题描述: GPT-SoVITS声音克隆如何训练和推理教程 解决方案: Zero-shot TTS: Input a 5-second vocal sample and experience instant text-to-speech conversion.零样本 TTS:输入 5 秒的人声样本并体验即时文本到语音转换…

基于java的CRM客户关系管理系统的设计与实现

本科毕业设计(论文) 题 目: 基于java的CRM客户关系管理系统的设计与实现 专题题目: 说 明 请按以下顺序编排: 封面任务书开题报告中外文摘要及关键词目录正文附录&…

算法学习笔记Day9——动态规划初探

一、介绍 本文解决几个问题:动态规划是什么?解决动态规划问题有什么技巧?如何学习动态规划? 1. 动态规划问题的一般形式就是求最值。动态规划其实是运筹学的一种最优化方法,只不过在计算机问题上应用比较多&#xff…

矩阵连乘算法

矩阵连乘&#xff1a; #include<iostream> #define inf 0x7fffffff using namespace std; int a[256] { 0 };//存储矩阵的行和列 int m[256][256] { 0 };//存储i到j的最少计算次数 int s[256][256] { 0 };//存储i到j的中转站k void m_print(int i, int j) {if (i …

Kubectl常见排查pod问题命令

一.查看命名空间pod及其日志 #查看命名空间pod kubectl get pods -n <命名空间名称> #该命令不加-n命名空间名称&#xff0c;默认是查看default命名空间的pod#查看对应pod的日志kubectl logs -f <pod-name> -n <namespace>#同样的如果查看的是default命名空…

如何在OpenWRT上配置SFTP远程文件传输

如何在OpenWRT上配置SFTP远程文件传输 OpenWRT 是一款广泛使用的开源路由器固件&#xff0c;它能够让普通的家用路由器具备高级路由功能&#xff0c;提供更多自定义和优化选项。本文将介绍如何在OpenWRT上配置SFTP&#xff08;SSH文件传输协议&#xff09;服务&#xff0c;以便…