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…

使用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组件允许…

使用Prometheus监控Padavan路由器

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

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

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

冲突域和广播域

文章目录 冲突域广播域 冲突域 在网络内部两个数据帧同时进行传输时&#xff0c;产生与发生冲突的区域&#xff0c;所有共享介质都是一个冲突域。冲突域时基于第一层&#xff0c;物理层的。 集线器和中继器因为都在物理层&#xff0c;没有MAC地址表&#xff0c;所以不能隔离冲…

数据结构之堆排序以及Top-k问题详细解析

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力 目录 1.前言 2.堆排序 2.1降序排序 2.2时间复杂…

Prime 1.0

信息收集 存活主机探测 arp-scan -l 或者利用nmap nmap -sT --min-rate 10000 192.168.217.133 -oA ./hosts 可以看到存活主机IP地址为&#xff1a;192.168.217.134 端口探测 nmap -sT -p- 192.168.217.134 -oA ./ports UDP端口探测 详细服务等信息探测 开放端口22&#x…

【Vulnhub 靶场】【HackathonCTF: 2】【简单】【20210620】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/hackathonctf-2,714/ 靶场下载&#xff1a;https://download.vulnhub.com/hackathonctf/Hackathon2.zip 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月20日 文件大小&#xff1a;2.6 GB 靶场作者&…

54.多级缓存

目录 一、传统缓存的问题、多级缓存方案。 二、JVM进程缓存。 1&#xff09;进程缓存和缓存。 2&#xff09;导入商品案例。 1.安装MySQL 2.导入SQL 3.导入Demo工程 4.导入商品查询页面 3&#xff09;初识Caffeine&#xff08;就是在springboot学过的注解方式的cache&…

NAND Flash和NOR Flash的异同

NAND Flash和NOR Flash是两种常见的闪存类型。 NOR Flash是Intel于1988年首先开发出来的存储技术&#xff0c;改变了原先由EPROM和EEPROM一统天下的局面。 NAND Flash是东芝公司于1989年发布的存储结构&#xff0c;强调降低每比特的成本&#xff0c;更高的性能&#xff0c;并…

栈和队列OJ题——15.循环队列

15.循环队列 622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09; * 解题思路&#xff1a; 通过一个定长数组实现循环队列 入队&#xff1a;首先要判断队列是否已满&#xff0c;再进行入队的操作&#xff0c;入队操作需要考虑索引循环的问题&#xff0c;当索引越界&…

网络接口规范

1、基本物理层: a) RJ45接口作为最基本的网络接口之一有两种形式&#xff1a;对于百兆网口有4条线&#xff0c;2对差分线&#xff1b;对于千兆网口有4对差分线。RJ45水晶头是有8个凹槽和8个触点&#xff08;8p8c&#xff09;的接头&#xff0c;分为集成网络变压器和非集成网络变…

2022年9月8日 Go生态洞察:Go Developer Survey 2022 Q2 结果分析

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

物奇平台电容触摸功能调试

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, 物奇平台电容触摸功能调试 1 修改按键驱动宏 2 编译生成wpk 文件,import 导入烧录文件。…

水果编曲软件fl studio手机版下载

fl studio mobile手机版中文名水果编曲软件&#xff0c;它是一款非常不错的音乐编曲软件&#xff0c;凭借简单易上手的操作方式&#xff0c;强悍且实用的功能&#xff0c;深受到了音乐创作者的喜爱&#xff0c;不仅仅提供了广阔的音乐创作空间&#xff0c;可以让用户对舞曲、轻…

工具网站:随机生成图片的网站

一个随机生成图片的网站&#xff1a;Lorem Picsum。 有时候&#xff0c;我们做静态页面需要大量图片去填充内容&#xff0c;以使用该网站去生成指定尺寸的图片。每次打开页面都会获取不同的图片&#xff0c;就不用我们做静态页面开发的时候&#xff0c;绞尽脑汁去找图片了。 …