JavaEE 多线程

JavaEE 多线程

文章目录

  • JavaEE 多线程
    • 引子
    • 多线程
      • 1. 特性
      • 2. Thread类
        • 2.1 概念
        • 2.2 Thread的常见构造方法
        • 2.3 Thread的几个常见属性
        • 2.4 启动一个线程
        • 2.5 中断一个线程
        • 2.6 等待一个线程
        • 2.7 获取当前线程引用
        • 2.8 休眠当前线程
      • 3. 线程状态

引子

当进入多线程这一块内容时,我们之前对代码的逻辑认知可能会被颠覆!

为什么这么说?让我们看看下面这段代码:

package demo1;public class Test1 {public static void main(String[] args) {boolean judge = true;while (judge) {System.out.println("你出不去了!!");}judge = false;System.out.println("我出来了!!");}
}

从单线程的角度,这是一个逻辑闭环,死循环后面的代码无法被执行,它"永远都出不去"!代码会一直陷入循环之中:在这里插入图片描述

但对于多线程来说,死循环也阻止不了它:

在这里插入图片描述

为什么能够做到这一点?这就涉及到多线程的特性了!

多线程

1. 特性

  • 每个线程都是一个独立的执行流
  • 多个线程之间是“并发”执行的

线程的调用方法我们使用lambda表达式(之前的文章有对其进行讲解),看看下面的代码:

package demo1;import static java.lang.Thread.sleep;public class Test3 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (true) {System.out.println("你好!");try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start(); // 在这里才开始创建线程while (true) {System.out.println("你也好!!");sleep(1000);}}
}

在这里插入图片描述

从上述执行结果可以看出,两个线程都在并发执行,大大提高了程序整体的运行效率!!

注:

  1. main函数本身为主线程

  2. 仔细观察可以发现,两个线程执行的先后顺序是不确定的,这和线程的"随机调度"有关:

    • 一个线程,什么时候被调度到cpu执行是不确定的
    • 一个线程,什么时候从cpu上下来,给其它线程让位是不确定的

    这种“随机调度”的特性很可能会造成"抢占式执行",从而造成线程安全问题

  3. 只有调用start()方法后t线程才会被创建

  4. **sleep()**方法可以对线程进行休眠,参数以毫秒为单位

2. Thread类

2.1 概念

Thread类是JVM用来管理线程的一个类,且每个线程都有一个唯一的Thread对象与之关联

每个执行流需要有一个对象来描述,而Thread类的对象就是用来描述一个线程执行流的,JVM会将这些Thread对象组织起来,用于线程调度,线程管理。

2.2 Thread的常见构造方法
方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用Runnable创建线程对象,并命名
Runnable runnable = new Runnable() {@Overridepublic void run() {}
};
Thread t1 = new Thread();
Thread t2 = new Thread(runnable);
Thread t3 = new Thread("线程3");
Thread t4 = new Thread(runnable, "线程4");
2.3 Thread的几个常见属性
属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台进程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID是线程的唯一标识,不同线程不会重复

  • 优先级高的线程理论上来说更容易被调度到

  • 是否存活,可以理解为run方法是否运行结束了

  • JVM会在一个进程的所有非后台线程结束后,才会结束运行

    // 默认线程t为前台线程,前台线程未结束时,整个进程不会结束
    package demo2;public class Test5 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for (int i = 0;i < 10;i++) {try {System.out.println(Thread.currentThread().getName() +  ":本线程还活着");Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(Thread.currentThread().getName() + ":本线程将消失");});System.out.println("ID-" + t.getId());System.out.println("名称-" + t.getName());System.out.println("状态-" + t.getState());System.out.println("优先级-" + t.getPriority());System.out.println("后台线程-" + t.isDaemon());t.start();for (int j = 0;j < 5;j++) {System.out.println("我是主线程");Thread.sleep(1000);}System.out.println("主线程结束了");}}
    

    在这里插入图片描述

    // 这里修改t为后台线程,则主线程结束整个进程就结束
    package demo2;public class Test5 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for (int i = 0;i < 10;i++) {try {System.out.println(Thread.currentThread().getName() +  ":本线程还活着");Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(Thread.currentThread().getName() + ":本线程将消失");});t.setDaemon(true); // 这里修改t为后台线程,则主线程结束整个进程就结束System.out.println("ID-" + t.getId());System.out.println("名称-" + t.getName());System.out.println("状态-" + t.getState());System.out.println("优先级-" + t.getPriority());System.out.println("后台线程-" + t.isDaemon());t.start();for (int j = 0;j < 5;j++) {Thread.sleep(2000);System.out.println("我是主线程");}System.out.println("主线程结束了");}
    }
    

    在这里插入图片描述

2.4 启动一个线程

之前我们通过覆写run方法创建了一个线程对象,但线程对象被创建出来并不代表着线程就开始运行了,我们需要通过调用start()方法才真正在操作系统的底层创建出一个线程:

public class Test6 {public static void main(String[] args) {Thread t = new Thread(()->{System.out.println("hh");});// 此时线程并未完全创建}
}

在这里插入图片描述

调用t.start()方法后,才算创建线程成功,同时自动调用run方法(被覆写):

在这里插入图片描述

2.5 中断一个线程

目前常见的中断线程方式有以下两种:

  1. 用共享的标记来进行沟通

    package demo2;public class Test7 {public static volatile boolean isQuit = false; // 这里需要给标志位加上volatile关键字public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!isQuit) {System.out.println("我是一个线程,正在工作中");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();for (int i = 5;i >= 0;i--) {System.out.println("倒计时: " + i);Thread.sleep(1000);}System.out.println("让线程结束工作");isQuit = true;System.out.println("线程工作结束!");}
    }
    

    在这里插入图片描述

  2. 使用thread对象的interrupted()方法来通知线程结束

    方法说明
    Thread.currentThread().isInterrupted()判断当前线程中断标志是否设置
    Thread.currentThread().Interrupt()设置中断标志中断该线程

    注:Thread.currentThread()操作是获取当前的线程实例(t),哪个线程调用,得到的就是哪个线程的实例

    package demo2;public class Test8 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {try {System.out.println("我是一个线程,正在工作中");Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();for (int i = 5;i >= 0;i--) {System.out.println("倒计时: " + i);Thread.sleep(1000);}System.out.println("让线程结束工作");t.interrupt();System.out.println("线程工作结束!");}
    }
    

    在这里插入图片描述

注:thread收到通知的方式有两种:

  1. 如果线程因为调用wait/join/sleep等方法而阻塞挂起,则以InterruptedException异常的形式通知,同时清除中断标准

    当出现InterruptedException的时候,要不要结束线程取决于catch中代码的写法,可以选择忽略这个异常,也可以通过break跳出循环结束线程;

  2. 如果只是内部的一个中断标志被设置,thread可以通过;

    Thread.currentThread().isInterrupted()判断指定线程的中断标志是否设置,不清除中断标志,这种方式通知收到的更及时,即使线程正在sleep也可以马上收到。

2.6 等待一个线程

有时候一个线程需要等待另一个一个线程完成它的工作后,才能进行自己的下一步工作。这个时候可以通过join()方法来进行线程等待。

join(): 在哪个线程中调用join方法则当前线程要等待引用线程执行完后才能执行,如在主线程中调用t.join();则主线程要等待t线程执行完后才能执行

package demo2;public class Test9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for (int i = 0;i < 5;i++) {System.out.println("t-线程工作中!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();t.join();System.out.println("main 执行了");}
}

在这里插入图片描述

join()方法也可以设置时间限制,最多等待X毫秒,时间一到线程就停止阻塞等待:

t.join(2000);

在这里插入图片描述

2.7 获取当前线程引用

public static Thread currentThread(); 返回当前线程对象的引用

package demo2;public class Test10 {public static void main(String[] args) {Thread thread = Thread.currentThread();System.out.println(thread.getName());}
}

在这里插入图片描述

2.8 休眠当前线程

public static void sleep (long millis) throws InterruptedException; 休眠当前进程millis毫秒

注:因为线程的调度是不可控的,所有这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的

package demo2;public class Test11 {public static void main(String[] args) throws InterruptedException {System.out.println("开始时间:" + System.currentTimeMillis());Thread.sleep(3000); // 休眠主线程System.out.println("结束时间:" + System.currentTimeMillis());}
}

在这里插入图片描述

3. 线程状态

  • NEW: Thread对象创建好了,但是还没有调用start方法在系统中创建线程;
  • RUNNABLE:就绪状态,表示这个线程正在cpu上执行或准备就绪随时可以去cpu上执行;
  • TIME_WATING: 表示指定时间的阻塞,达到一定时间后会自动解除阻塞,一般是调用sleep方法或有时间参数的join方法时会进入该状态;
  • WATINGT: 表示不带时间的阻塞(死等),必须要满足一定条件才会解除阻塞,一般调用wait方法join方法会进入该状态;
  • BLOCKED: 锁竞争引起的阻塞;
  • TERMINATED: Thread对象仍然存在,但是系统内部的线程已经执行完毕了

在这里插入图片描述

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

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

相关文章

ArrayList 与 顺序表 (附洗牌算法)!

曾经我也是一枚学霸&#xff0c;直到有一天想去学渣的世界看看&#xff0c;结果就找不到回去的路了。 目录 1. 线性表 2.顺序表 2.1 接口的实现 3. ArrayList简介 4. ArrayList使用 4.1 ArrayList的构造 4.2 ArrayList常见操作 4.3 ArrayList的遍历 4.4 ArrayList的扩…

掌握视频剪辑技巧:批量置入视频封面,提升视频品质

在当今数字化时代&#xff0c;视频已成为生活的重要组成部分。无论是观看电影、电视剧、综艺节目&#xff0c;还是分享个人生活、工作成果&#xff0c;视频都以其独特的魅力吸引着大众的视线。视频封面是视频内容的缩影&#xff0c;是观众对视频的第一印象。一个好的封面能吸引…

C/C++学生选课/排课系统[2023-12-3]

问题描述&#xff1a;根据我校自动化专业的部分必修及选修课信 息&#xff0c;设计一个学生选课/排课系统。 基本要求&#xff1a; 1、从文件读入课程信息&#xff1b; 2、从键盘输入拟添加的选修课信息&#xff1b; 3、删除已选的选修课(1门或多门) &#xff1b; 4、输出已…

C++ 学习笔记——C++纯虚函数和抽象类

C纯虚函数 什么是纯虚函数 1&#xff0c;纯虚函数只有函数名、参数、返回值类型。 2&#xff0c;纯虚函数的定义是在函数句首使用 virtual 关键字修饰&#xff0c;并且在句末增加 “ 0”。 virtual void funtion() 0;3&#xff0c;纯虚函数只有声明&#xff0c;基类可以存…

Kubernetes(K8s)_15_CNI

Kubernetes&#xff08;K8s&#xff09;_15_CNI CNI网络模型UnderlayMAC VLANIP VLANDirect Route OverlayVXLAN CNI插件FlannelCalico CNI配置内置实现 CNI CNI(Container Network Interface): 实现容器网络连接的规范 Kubernetes将网络通信可分为: Pod内容器、Pod、Pod与Se…

gRPC Java、Go、PHP使用例子

文章目录 1、Protocol Buffers定义接口1.1、编写接口服务1.2、Protobuf基础数据类型 2、服务器端实现2.1、生成gRPC服务类2.2、Java服务器端实现 3、java、go、php客户端实现3.1、Java客户端实现3.2、Go客户端实现3.3、PHP客户端实现 本文例子是在Window平台测试&#xff0c;Ja…

解决 requests.post 数据字段编码问题的方法

目录 一、引言 二、问题分析 三、解决方案与技术解析 四、总结 一、引言 在 Python 中&#xff0c;requests 库是一个常用的 HTTP 客户端库&#xff0c;可以用于发送所有类型的 HTTP 请求。当我们使用 requests.post 方法发送 POST 请求时&#xff0c;我们可能会遇到数据字…

Hdoop学习笔记(HDP)-Part.06 安装OracleJDK

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

ELK高级搜索,深度详解ElasticStack技术栈-上篇

前言 视频地址&#xff1a;java中级教程-ELK高级搜索&#xff0c;深度详解ElasticStack技术栈ELK高级搜索&#xff0c;深度详解ElasticStack技术栈-下篇本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除 1. 课程简介 1.1 课程内容 ELK是包含但不限于Elas…

Springboot启动原理解析

我们开发任何一个Spring Boot项目&#xff0c;都会用到如下的启动类 SpringBootApplication public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);} } 从上面代码可以看出&#xff0c;Annotation定义&#x…

【Web安全】拿到phpMyAdmin如何获取权限

文章目录 1、outfile写一句话2、general_log_file写一句话 通过弱口令拿到进到phpMyAdmin页面如何才能获取权限 1、outfile写一句话 尝试执行outfile语句写入一句话木马 select "<?php eval($_REQUEST[6868])?>" into outfile "C:\\phpStudy\\WWW\\p…

多项式拟合求解

目录 简介 基本原理 例1 例2 例3 参考资料 简介 多项式拟合可以用最小二乘求解&#xff0c;不管是一元高阶函数&#xff0c;还是多元多项式函数&#xff0c;还是二者的混合&#xff0c;都可以通过统一的方法求解。当然除了最小二乘法&#xff0c;还是其他方法可以求解&…

springboot数据源配置

springboot数据源配置 数据层解决方案——持久化技术 内置持久化解决方案——jdbcTemplate 内置数据库 H2一般用于测试环境&#xff0c;配置profiels&#xff0c;只在开发阶段使用&#xff0c;让他在上线的时候不走这里就可以了 要使用内嵌的数据库H2,要先导入jar包

sCrypt 现已支持各类主流前端框架

sCrypt 现已支持各类主流前端框架&#xff0c;包括&#xff1a; ReactNext.jsAngularSvelteVue 3.x or 2.x bundled with Vite or Webpack 通过在这些支持的前端框架中集成sCrypt开发环境&#xff0c;你可以直接在前端项目里访问合约实例和调用合约&#xff0c;方便用户使用Se…

30、LCD1602

LCD1602介绍 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是一种字符型液晶显示模块&#xff0c;可以显示ASCII码的标准字符和其它的一些内置特殊字符&#xff0c;还可以有8个自定义字符 显示容量&#xff1a;162个字符&#xff0c;每个字符为5*7点阵 …

智能优化算法应用:基于和声算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于和声算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于和声算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.和声算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

pybind11教程

pybind11教程 文章目录 pybind11教程1. pybind11简介2. cmake使用pybind11教程3. pybind11的历史 1. pybind11简介 项目的GitHub地址为&#xff1a; pybind11 pybind11 是一个轻量级的头文件库&#xff0c;用于在 Python 和 C 之间进行互操作。它允许 C 代码被 Python 调用&am…

力扣每日一题day26[42. 接雨水]

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] …

Day12 qt QMianWindow,资源文件,对话框,布局方式,常用ui控件

QMianWindow 概述 QMainWindow 是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏&#xff08; menu bar &#xff09;、多 个工具栏 (tool bars) 、多个铆接部件 (dock widgets) 、一个状态栏 (status bar) 及 一个中心部件 (central widget) 许多应用程序的基础…

算法设计与实现--贪心篇

贪心算法 贪心算法是一种在每一步选择中都采取当前状态下最优决策的算法&#xff0c;以期望能够通过一系列局部最优的选择达到全局最优。贪心算法的关键是定义好局部最优的选择&#xff0c;并且不回退&#xff0c;即一旦做出了选择&#xff0c;就不能撤销。 一般来说&#xf…