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.mzph.cn/news/194026.shtml

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

相关文章

2023-12-03 LeetCode每日一题(可获得的最大点数)

2023-12-03每日一题 一、题目编号 1423. 可获得的最大点数二、题目链接 点击跳转到题目位置 三、题目描述 几张卡牌 排成一行&#xff0c;每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。 每次行动&#xff0c;你可以从行的开头或者末尾拿一张卡牌&#x…

【Python/Java/C++三种语言】20天拿下华为OD笔试之【哈希表】2023B-单词接龙【欧弟算法】全网注释最详细分类最全的华为OD真题题解

文章目录 题目描述与示例题目描述输入描述输出描述示例一输入输出说明 示例二输入输出说明 解题思路代码PythonJavaC时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 题目描述与示例 题目描述 单词接龙的规则是&#xff1a; 可用于接龙的单词首字母必须要前一个单词的…

虚拟机扩容磁盘/dev/mapper/centos-root

虚拟机扩容磁盘/dev/mapper/centos-root 1. 在虚拟机管理界面增加磁盘大小或添加磁盘 2. 扩容操作 lsblk -l # 下面/dev/sdb 每个人的可能不同&#xff0c;有/dev/sda 或 /dev/sdc&#xff0c;根据实际更改 fdis /dev/sdb m n p 1 回车 回车 wfdisk -l pvdisplay pvcrea…

Vue.js中v-if 和 v-show 的区别

1、v-if: v-if 指令是 Vue.js 中的一个条件渲染指令。 当 v-if 的条件为 false 时&#xff0c;元素及其内容将从 DOM 中完全移除。 适用于你期望大部分时间条件为 false&#xff0c;或者当你希望通过完全移除元素来节省资源时。 2、v-show: v-show 指令同样也是一个条件渲染…

信息流广告行为兴趣定向底层逻辑算法

行为兴趣定向 1: 行为兴趣的背后是计划的数据 行为是用户在平台的动作&#xff1a;点赞、评论、分享、点击、下单、成交等&#xff0c;用户发生过的标签 兴趣不一定发生&#xff0c;我有打高尔夫的兴趣&#xff0c;但是从来没打过&#xff0c;因为穷 系统会根据用户的行为标…

C++STL中的string容器

string 容器基本概念 C 风格字符串 ( 以空字符结尾的字符数组 ) 太过复杂难于掌握&#xff0c;不适合大程序的开发&#xff0c; 所以 C 标准库定义了一种 string 类&#xff0c;定义在头文件。 String 和 c 风格字符串对比&#xff1a; u Char*是一个指针&#xff0c; …

【C++ Primer Plus学习记录】第5章编程练习

1.编写一个要求用户输入两个整数的程序。该程序将计算并输出这两个整数之间&#xff08;包括这两个整数&#xff09;所有整数的和。这里假设先输入较小的整数。例如&#xff0c;如果用户输入的是2和9&#xff0c;则程序将指出2~9之间所有整数的和为44。 //5.9 1 #if 1 #includ…

使用OpenMVS重建模型

1、数据格式转换 首先将生成的稠密点云以及图片信息转换成openmvs支持的.mvs文件。在openmvs_sample中的bin文件内打开终端 作者&#xff1a;舞曲的小水瓶 https://www.bilibili.com/read/cv25019877/ 出处&#xff1a;bilibili interfaceCOLMAP.exe -i D:\desktop\test\toy\…

【Linux服务器Java环境搭建】05 Node JS安装及环境变量配置

【Linux服务器Java环境搭建】01购买云服务器以及在服务器中安装Linux系统 【Linux服务器Java环境搭建】02 通过xftp和xshell远程连接云服务器 【Linux服务器Java环境搭建】03 Git工具安装 【Linux服务器Java环境搭建】04 JDK安装&#xff08;JAVA环境安装&#xff09; 【Linux服…

flink源码分析 - 命令行参数解析-CommandLineParser

flink版本: flink-1.11.2 调用位置: org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint#main 代码位置: flink核心命令行解析器: org.apache.flink.runtime.entrypoint.parser.CommandLineParser /** Licensed to the Apache Software Foundati…

基于OpenAPI工具包以及LSTM的CDN网络流量预测

基于LSTM的CDN网络流量预测 本案例是基于英特尔CDN以及英特尔 OpenAPI Intel Extension for TensorFlow* Intel oneAPIDPC Library 的网络流量预测&#xff0c;CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;目的是将源站内容分发至最接近用户的节点&#xff0c;使用…

unity学习笔记17

一、动画组件 Animation Animation组件是一种更传统的动画系统&#xff0c;它使用关键帧动画。你可以通过手动录制物体在时间轴上的变换来创建动画。 一些重要的属性&#xff1a; 1. 动画&#xff08;Animation&#xff09;&#xff1a; 类型&#xff1a; Animation组件允许…

java为什么要设计8个基本数据类型的封装类型?

Java中的基本数据类型包括byte、short、int、long、float、double、boolean和char。然而&#xff0c;这些基本数据类型并非对象&#xff0c;他们只是简单的数值&#xff0c;无法调用方法。 为了能在Java这种面向对象的语言中更好地操作这些数值&#xff0c;Java设计了对应的8个…

换股解套策略

在股市中&#xff0c;投资者难免会遇到被套的情况。面对这种情况&#xff0c;如何进行换股策略以降低损失并寻求反弹的机会呢&#xff1f;本文将为您详细解析。 一、了解被套的原因 在进行换股策略之前&#xff0c;首先要了解被套的原因。一般来说&#xff0c;被套的原因有以下…

使用Prometheus监控Padavan路由器

Prometheus监控Padavan路由器 1、背景 近期在Synology&#xff08;群辉&#xff09;中安装一套Prometheus监控程序&#xff0c;目前已经监控Synology&#xff0c;然后家中有有路由器&#xff08;Padavan&#xff09;型号&#xff0c;也准备使用PrometheusGrafan进行监控。 ‍…

1、STM32F407 LED Demo

#ifndef、#define、#endif格式条件编译&#xff0c;作用是避免头文件内容比重复定义 main.c #include "stm32f4xx.h" #include "led.h" #include "delay.h" //CPU主时钟168MHz int main(void) {delay_init(168);LED_Init();while(1){GPIO_SetB…

Python 读取电子发票PDF 转成Excel

Python 读取电子发票PDF 转成Excel 目录 0.前提 1.python相关的处理PDF的库 2.实际好用的 3.实际代码 4.思考 0.前提 只识别普通电子发票PDF&#xff0c;提取其中某些关键内容到excel中。 1.python相关的处理PDF的库 如下4个库是经常更新维护的&#xff01; pyP…

采集工具-免费采集器下载

在当今信息时代&#xff0c;互联网已成为人们获取信息的主要渠道之一。对于研究者和开发者来说&#xff0c;如何快速准确地采集整个网站数据是至关重要的一环。以下将从九个方面详细探讨这一问题。 确定采集目标 在着手采集之前&#xff0c;明确目标至关重要。这有助于确定采集…

企业数字化的思考

1. 企业信息化 1.1 从0到1构建信息系统 随着it基础的不断成熟与在企业业务中的应用&#xff0c;企业构建专业化的信息系统已不再需要太多的讨论&#xff0c;基本都在基于自身的阶段构建各种各样的业务支撑系统&#xff0c;从OA\CRM\财务系统\HR\ERP\SAP等到类似更为专项的合同…

flink源码分析 - standalone模式下jobmanager启动过程配置文件加载

flink版本: flink-1.11.2 代码位置: org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint#main /** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements. See the NOTICE file* distributed with t…