【Java多线程编程】Thread类

news2024/10/7 11:21:29

Thread类是什么?

Thread 类是 Java 提供的一个标准库,我们可以通过 Thread 类进行多线程编程。因此,今天我给大家讲解的是如何使用 Thread 类进行线程编程。

详细讲解 Thread 类中的:lambda 表达式、start 方法(启动线程)、sleep 方法(休眠线程)、currentThread 方法(返回当前线程的实例)、isInterrupted 方法(标志位false)、Interrupted 方法(标志位true)。

目录

1. Thread类创建线程

2. 启动线程strat()方法

3. 线程的等待join()方法

4. 获取当前线程的引用

5. 休眠当前线程

6. 中断一个线程

1. Thread类创建线程

Thread 类创建线程,我们得实例化一个 Thread 类对象。实例化 Thread 类对象来创建进程我们使用最多的就是 lambda 表达式。lambda 表达式语法简单表示为:()->{}

因此,我们可以这样创建一个线程:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("这是thread引用内的线程");  
        });
    }

以上代码,实例化了 Thread类的引用 thread,并在 thread 引用内创建了一个线程,线程为一句话“这是thread引用内的线程”。

当然,main 方法在上述代码中作为主线程,thread 引用作为子线程

我们通常创建线程使用的就是 lambda 表达式,但创建线程还有其他几种方式,创建线程的几个基本方式在这篇文章中有详细讲解:创建线程的基本方式


2. 启动线程strat()方法

当我们创建好一个线程后,我们可以使用 start() 方法来启动这个线程。start() 方法是 Thread 类里面的一个方法作用是启动线程,因此我们可以通过 Thread 类的引用来调用 start() 方法来启动线程。

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("这是thread引用内的线程");
        });
        thread.start();
    }

运行后打印:

通过以上代码,我们可以了解到启动线程的方式是调用 start()方法。在启动线程的过程中,start() 方法作为线程的启动、thread 引用作为线程的入口

  • 在 thread 引用创建后,thread 引用内的 线程并为启动,因此不存在操作系统中
  • 调用 start() 方法后,才真正在操作系统底层创建一个线程

3. 线程的等待join()方法

当子线程、主线程里面都有相应的线程时,我们不能保证是子线程内的线程先执行还是主线程内的线程先执行,如下代码:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");//thread引用内的线程
        });
        thread.start();//启动thread引用内的线程
        System.out.println("Hello main");//main方法里面的线程
    }

以上代码可能输出 Hello thread、Hello main或者Hello main、Hello thread。

假设我们设计上述代码的初衷就是先输出 Hello thread 后输出 Hello main,这样就不能达到需求。

因此,我们可以使用 Thread 类底下的 join() 方法来等待当前线程执行完毕后,再执行后序线程

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("Hello thread");//run方法里面的线程
            }
        };
        thread.start();//启动线程
        thread.join();//等待线程
        System.out.println("Hello main");//main方法里面的线程
    }

运行后输出:

以上代码中调用了 join() 方法,使得 main 线程中的 thread 先执行完毕后再继续往下执行。因此就能到先输出 Hello thread 后输出 Hello main 的效果。

注意,我们在调用 join() 方法后,需要使用 Alt+Enter join() 来抛出 throws InterruptedException异常。这样就能很好的调用join() 方法。

  • main 线程中 thread 调用 join() 方法的时,如果 thread 还在运行,此时的 main 线程会发生阻塞,直到 thread 执行完毕后 main 方法继续往下执行。
  • main 线程 中 thread 调用 join() 方法时,如果 thread 已经结束,此时的 join() 方法不会造成阻塞,main 线程继续执行!

4. 获取当前线程的引用

获取当前线程的引用,我们可以 currentThread() 方法返回当前线程对象的引用:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");
            System.out.println("thread里面的线程: "+Thread.currentThread());
        });
        thread.start();//启动thread引用内的线程
    }

运行后输出:

 Thread[Thread-0,5,main]的含义为:

  1. Thread-0代表线程名
  2. 5指的是线程优先级为5(优先级默认为5)
  3. main指的是线程所处的线程组

线程名默认为 Thread-0 ,我们可以通过初始化的方式来设置线程名。设置方法在 lambda 表达式后设置,格式为: ()->{},线程名。如下代码:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");
            System.out.println("thread里面的线程: "+Thread.currentThread());
        },"thread线程");
        thread.start();//启动thread引用内的线程
    }

运行后输出:


也可以通过线程调用 getName() 方法来获取线程的名字。如下方代码所示:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");
        },"thread线程");
        thread.start();//启动thread引用内的线程
        thread.join();//等待thread线程执行完毕
        System.out.println(thread.getName());//输出thread的线程名
    }

运行后输出:


5. 休眠当前线程

休眠当前线程的方式为调用 sleep() 这个方法,sleep() 方法是 Thread 类底层的一个方法,用来休眠线程。sleep() 方法里面要加参数,参数值为毫秒单位,1秒对应1000毫秒

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            int flag = 3;//线程运行次数
            while (flag > 0) {
                System.out.println("thread线程");
                try {
                    Thread.sleep(1000);//休眠一秒钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                flag--;
            }
        });
        thread.start();//启动线程
    }

运行后输出:

以上程序,通过 sleep(1000) 休眠了1秒线程,总共休眠了三次。最后输出三条语句,大家可以自行尝试一下感受停顿感。

注意,当我们调用 sleep 方法时会出现的 Unhandled exception... 这个异常意为:未处理中断异常

因此,我们可以 Alt+Enter sleep() 方法。来 try/carch 这个异常。大家不必关注 catch 内的内容 e.printStackTrace() 意为打印当前位置的调用栈,后期深入学习则会更加理解。 


6. 中断一个线程

举个例子:张三是一个线程,线程内容为:输入转账金额,输入密码,转账。

有一天,张三在微信上给陌生人转账,此时张三进入了工作状态。当达到输入密码步骤时,警察蜀黍给张三打个了电话要求张三停止输入密码,避免损失金钱。此时警察的阻止中断了张三这个线程。这样的一个例子就是中断线程的操作,那么在 Java 中如果我们想要中断一个线程该如何去做呢?

中断一个线程意味停止一个线程,并非终止一个线程。本质上来说,让一个线程终止,就是让该线程的入口方法提前执行完毕。因此,可设置一个标志位来达到该效果。


当我们使用循序运行一个线程:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            while (true) {
                System.out.println("Hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }

当我们写出以上代码时,会一直打印 Hello thread。如果我们想要特定的时候线程就中断了,可以设置一个特定的标志位来达到该效果。

把 while 循序中的条件自定义为一个特定的标志位 isExit ,然后通过特定限制来达到中断效果,如以下代码:

public class TestDemo {
    public static boolean isExit = false;//全局变量isExit
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()-> {
            while (!isExit) {
                System.out.println("Hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();//启动thread线程
        
        Thread.sleep(3000);//main线程里面的sleep方法
        isExit = true;//修改全局变量isExit
    }
}

运行后输出:

以上代码中,thread 线程内的 sleep() 和 main 线程中的 sleep() 方法在操作系统中几乎是同时运行的,因此当 main 线程中的 sleep() 达到 3 秒钟后标志位 isExit 修改为 true 最后程序结束,输出了三条语句。

注意,标志位是全局变量,如果标志位是局部变量,lambda 表达式识别不出这个标志位。


当然,在 Thread 类内部提供了一个标志位可通过 isInterrupted() 方法,它的默认值为 false。和 Interrupted() 方法默认为 true,来中断线程,因此可以写出以下代码:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()-> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();//启动thread线程

        Thread.sleep(3000);//main线程里面的sleep方法
        
        thread.interrupt();//interrupt把标志位设置为true
    }

运行后输出:

通过上述程序我们发现,打印三条语句后抛出了一个异常,继续无限打印 Hello thread。

解释:

抛出的异常为 catch 中的 e.printStackTrace() 意为打印当前位置的调用栈

interrupt 方法在线程阻塞(如正在执行 sleep 方法)时就会把阻塞状态唤醒也就会抛出一个异常让 sleep 方法立即结束。 

当 sleep 方法被唤醒时,sleep 会自动把标志位清空,也就是把通过 Interrupted 方法设置好的标志位的 true 清空,因此标志位恢复为 false,这样就会导致线程继续往下执行。

好比一个开关,有的开关按下去了就一直保持按下去的状体,有的开关按下去了会自动把按钮弹起来(类似于sleep方法)。

因此,我们直接在 catch 中加上一个 break 语句,这样在抛出异常后循环就会结束。

catch (InterruptedException e) {
        e.printStackTrace();
        break;
    }

🧑‍💻作者:程序猿爱打拳,Java领域新星创作者,阿里云社区博客专家。

🗃️文章收录于:Java多线程编程

🗂️JavaSE的学习:JavaSE

🗂️Java数据结构:数据结构与算法

  

本篇博文到这里就结束了,感谢点赞、收藏、关注~

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

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

相关文章

WiFi(Wireless Fidelity)基础(七)

目录 一、基本介绍(Introduction) 二、进化发展(Evolution) 三、PHY帧((PHY Frame ) 四、MAC帧(MAC Frame ) 五、协议(Protocol) 六、安全&#x…

Cloud Kernel SIG月度动态:发布ANCK 5.10、4.19新版本,ABS新增仓库构建功能

Cloud Kernel SIG(Special Interest Group):支撑龙蜥内核版本的研发、发布和服务,提供生产可用的高性价比内核产品。 01 SIG 整体进展 发布 ANCK 5.10-014 版本。 发布 ANCK 4.19-027.2 版本。 ABS 平台新增 OOT 仓库临时构建功…

如何远程控制电脑?3个方法轻松搞定!

案例:如何远程控制电脑? 【我不想时时刻刻都带着自己的电脑,听朋友说可以远程电脑。有没有大神分享一下具体的操作方法?感谢!】 随着科技的不断进步,远程控制电脑已经不再是一件难以实现的事情。如今&…

09.python可视化-Seanorn绘制类别关系图boxplot() boxenplot() violinplot()

分类散点图 分类分布图 1). 箱线图 : boxplot() 2).增强箱图boxenplot() 3).小提琴图 :violinplot() 分类统计图 2. 分类分布图 1). 箱线图 应用场景:主要用来显示与类别相关的数据分布。 seaborn.boxplot(xNone, yNone, hueNone, dataNone, orderNone, hue_orde…

GoView 是一个Vue3搭建的低代码数据可视化开发平台

一、总览 开源、精美、便捷的「数据可视化」低代码开发平台 二、整体介绍 框架:基于 Vue3 框架编写,使用 hooks 写法抽离部分逻辑,使代码结构更加清晰; 类型:使用 TypeScript 进行类型约束,减少未知错误…

WiFi(Wireless Fidelity)基础(九)

目录 一、基本介绍(Introduction) 二、进化发展(Evolution) 三、PHY帧((PHY Frame ) 四、MAC帧(MAC Frame ) 五、协议(Protocol) 六、安全&#x…

MySql -- 事务

目录 1.概念 2.事务的运用场景 3.事务的四大特点 4.执行事务带来的问题 4.1 脏读 4.2 不可重复度 4.3 幻读 5. MySQL中事务的隔离级别 1.概念 事务就是把若干个独立操作打包成一个整体而诞生的一种功能. 2.事务的运用场景 比如:A——>B 转账500 A的余额-500…

【Qt编程之Widgets模块】-007:QStandardPaths类使用方法

1 头文件&#xff1a; #include <QStandardPaths>2 详细说明 QStandardPaths类提供用于访问标准路径的方法&#xff0c;该类包含用于查询本地文件系统上的标准位置的函数&#xff0c;用于常见任务&#xff0c;如特定于用户的目录或系统范围的配置目录。 所谓系统标准路…

[pgrx开发postgresql数据库扩展]6.返回序列的函数编写(1)单值序列

上篇文章是中规中矩的标准计算函数&#xff0c;就算不用pgrx&#xff0c;也是可以正常理解的&#xff0c;所以基本上没有什么对于pgrx框架有关系的东西&#xff08;唯一有关系的东西&#xff0c;应该就是Rust的时间类型与pgrx的时间类型的计算了&#xff09;。 这篇文章会讲一…

Java面试(1)Java概述

文章目录 Java 概述1.什么是Java2. JDK1.5 之后的三大版本3. Jdk和Jre和JVM的区别4. 什么是跨平台性&#xff1f;原理是什么5. Java 语言有哪些特点?6. 什么是字节码&#xff1f;采用字节码的最大好处是什么7. 为什么不全部使用 AOT&#xff08;since JDK9&#xff09; 呢&…

马赛克处理

去取马赛克的网址&#xff1a; Redact • Photo - Free And Private Image Redaction In The Browser https://redact.photo/ REDACT.PHOTO &#xff08;照片马赛克处理在线工具&#xff09;简介 REDACT.PHOTO是一个照片马赛克处理在线工具&#xff0c;能够帮助我们非常方便…

2023自动化测试的10个最佳实践(建议收藏)

虽然大家都知道坚果是非常健康和有营养的&#xff0c;但是&#xff0c;当你尝试吃它的时候&#xff0c;我猜测过程都不会很顺利。现实就是那么相似&#xff0c;我们都知道测试自动化对软件开发有好处&#xff08;就像坚果对我们的身体一样&#xff01;&#xff09;&#xff0c;…

arcgis插件-带属性TXT转SHP数据(支持独立图层、追加到图层)

20230512记录更新 arcgis插件-带属性TXT转SHP数据&#xff08;支持独立图层、追加到图层&#xff09; 这个版本省略掉新建面图层&#xff0c;再在界面进行选择图层的操作。 界面简化到只需要一步操作&#xff0c;选择&#xff08;或者复制&#xff09;TXT文件所在路径&#x…

机器学习(二)决策树原理剖析及python实现

本篇介绍第二个机器学习算法&#xff1a;决策树算法&#xff0c;我们经常使用决策树处理分类问题&#xff0c;近来的调查表明决策树也是最经常使用的数据挖掘算法。 图1所示的流程图就是一个决策树&#xff0c;长方形代表判断模块&#xff08;decision block&#xff09;&…

[Android]AsyncChannel介绍

背景 在WifiManager.java中&#xff0c;随处可见这样的方法调用实现&#xff1a; 代码路径&#xff1a;frameworks/base/wifi/java/android/net/wifi/WifiManager.java public void connect(int networkId, ActionListener listener) {...getChannel().sendMessage(CONNECT_NE…

你真的会跟 ChatGPT 聊天吗?(下)

接《你真的会跟 ChatGPT 聊天吗&#xff1f;&#xff08;上&#xff09;》&#xff0c;本文下半部分没有无毛猫那么搞笑的内容啦&#xff01;即使如此&#xff0c;哪怕你对文中提及的技术不大了解&#xff0c;也可以毫无压力地看完这篇&#xff0c;描述如何更好地获得 ChatGPT …

视频怎么压缩到20M以内

视频怎么压缩到20M以内&#xff1f;我们知道在很多聊天软件中是限制传输的内容大小的&#xff0c;比如说视频大小会限制20M以内。还有就是我们在发一些邮件时&#xff0c;我们在上传附件的时候也是限制视频大小在20M以内。所以说我们有需要把视频压缩到20M以内的情况。那么针对…

记录一次Windows7操作系统渗透测试

#本文档仅用于实验&#xff0c;请勿用来使用恶意攻击&#xff01; 《中华人民共和国网络安全法》中&#xff0c;恶意破坏计算机信息系统罪在第二十七条被明确规定&#xff0c;规定内容为&#xff1a; 第二十七条 任何单位和个人不得为达到破坏计算机信息系统安全的目的&#x…

Linux发行版新秀:基于Ubuntu、系统核心 “不可变”

Vanilla OS 是近期才公开发布的 Linux 发行版&#xff0c;基于 Ubuntu 构建&#xff0c;免费且开源&#xff0c;默认桌面环境是 GNOME。虽然 Vanilla OS 的底层是 Ubuntu&#xff0c;但它并不是简单地基于 Ubuntu 进行 "remix"&#xff0c;而且外观看起来也不像 Ubun…

FreeRTOS:列表和列表项

要想看懂 FreeRTOS 源码并学习其原理&#xff0c;有一个东西绝对跑不了&#xff0c;那就是 FreeRTOS 的列表和列表项。列表和列表项是FreeRTOS的一个数据结构&#xff0c; FreeRTOS 大量使用到了列表和列表项&#xff0c;它是 FreeRTOS 的基石。要想深入学习并理解 FreeRTOS&am…