Java多线程:常见的线程的创建方法及Thread类详解

news2024/12/31 5:44:31


目录

一.并发编程相关概念

线程与进程

多线程

Java中线程的状态

二.线程的创建方法

方法一:继承Thread类

方法二:实现Runnable接口

其他方法

三.Thread类详解

Thread常见构造方法

Thread常见属性

Thread常见方法

start() 与 run() 

sleep() 与 yield() 

join() 

inerrupt() 


一.并发编程相关概念

线程与进程

线程是程序的执行流程的最小单元。一个进程(程序的执行实例)可以由一个或多个线程组成,每个线程都有自己的执行路径和执行状态。线程可以并发执行,即多个线程可以同时在不同的处理器核心或计算机上运行,从而提高程序的运行效率。

线程与进程的区别在于,进程是操作系统对一个正在运行的程序的抽象,而线程是进程内部的一个执行单位。一个进程可以有多个线程,这些线程共享进程的资源,如内存空间、文件描述符等。线程之间可以通过共享内存的方式进行通信,相比于进程间通信(如管道、消息队列)的开销更小。

多线程

对于多线程,我们可以举出这样的一个例子来帮助我们理解

一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴纳社保。 如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找 来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队, 自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。 此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别 排队执行。

 对于这样的业务场景,张三、李四和王五各自都相对于一个线程,多个线程之间相互配合才促使了整体业务流程的顺利进行,由此可见多线程对于任务处理的高效。其中由于李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread),李四和王五则为其他线程。

Java中线程的状态

Java中线程的状态有以下几种:

1. 新建(New):线程被创建但还没有开始执行。

2. 就绪(Runnable):线程被调度并准备开始执行,但还没有获取CPU执行权。

3. 运行(Running):线程正在执行任务。

4. 阻塞(Blocked):当线程执行到某个阻塞操作时,如等待IO操作完成或等待某个锁的释放时,线程会进入阻塞状态。

5. 等待(Waiting):线程执行了Object类的wait()方法,或者Thread类的join()方法时,线程会进入等待状态。

6. 超时等待(Timed Waiting):线程执行了Thread类的sleep()方法或等待超时后,线程会进入超时等待状态。

7. 终止(Terminated):线程执行完任务后或者出现异常终止时,线程进入终止状态。

二.线程的创建方法

线程是操作系统中的概念,操作系统内核实现了线程这样的机制,并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库)

而Java标准库中 Thread 类,便可以视为是对操作系统提供的 API 进行了进一步的抽象和封装,作为Java程序员就可以利用Thread 类来实现并发编程。

并发编程是指在计算机系统中,多个独立的任务同时进行,每个任务由一个或多个线程执行,并且这些线程可能在同一时刻同时运行。并发编程可以提高系统的执行效率和资源利用率。在并发编程中,多个线程可以同时进行不同的操作,比如读写数据、计算、网络通信等,它们可以同时执行,不需要等待其他线程的完成。常见的并发编程模型有多线程、异步编程、并行计算等。

说了这么多,归根结底还得落实到代码上,我们常见的创建线程的方式有俩种。

方法一:继承Thread类

  1. 创建一个继承自Thread类的子类。
  2. 在子类中重写run()方法,定义线程的执行逻辑。
  3. 在主线程中创建子类对象,并调用start()方法启动线程。
public class MyThread extends Thread {
    public void run() {
        // 线程执行逻辑
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

方法二:实现Runnable接口

  1. 创建一个实现了Runnable接口的类,并实现接口中的run()方法,定义线程的执行逻辑。
  2. 在主线程中创建Runnable实例,并将其作为参数传递给Thread类的构造方法。
  3. 调用Thread对象的start()方法启动线程。
public class MyRunnable implements Runnable {
    public void run() {
        // 线程执行逻辑
    }

    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

无论是继承Thread类还是实现Runnable接口,都可以创建多个线程并同时运行,以实现并发执行的效果。

其他方法

除此之外,使用匿名内部类或lambda表达式可以更快速的创建线程

匿名内部类创建Thread 子类对象

// 使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Thread 子类对象");
   }
};

匿名内部类创建 Runnable 子类对象

// 使用匿名类创建 Runnable 子类对象
Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Runnable 子类对象");
   }
});

 lambda 表达式创建Runnable 子类对象

// 使用 lambda 表达式创建 Runnable 子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
Thread t4 = new Thread(() -> {
    System.out.println("使用匿名类创建 Thread 子类对象");
});

三.Thread类详解

不管是上述创建线程中的哪一种方法,归根结底都是由 Thread 类延申开来的,Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

Thread常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用Runnable对象创建线程对象,并命名
Thread(ThreadGroup group, Runnable target)线程可以被用来分组管理,分好的组即为线程组

Thread常见属性

属性获取方法

ID

getId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

其中 ID 是线程的唯一标识,不同线程不会重复,优先级高的线程理论上来说更容易被调度到,是否存活,简单的理解的话就是 run 方法是否运行结束了

Thread常见方法

start() 与 run() 

  • start()方法是Thread类中的一个方法,用于启动一个新的线程。当调用start()方法时,系统会创建一个新的线程,并在新的线程中执行run()方法的内容。start()方法会在新的线程中执行一些准备工作,然后调用run()方法。
  • run()方法是实现了Runnable接口的类中的一个方法。在启动一个线程后,系统会自动调用该线程对象的run()方法。run()方法中包含了线程的主体代码,即线程要执行的任务。

前文中我们已经了解了如何通过重写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。重写 run 方法是提供给线程要做的事情的指令清单,线程对象可以认为是把李四、王五叫过来了,而调用start() 方法,就是喊一声:“行动起来!”,线程才真正独立去执行。

public class MyThread extends Thread {
    public void run() {
        // 线程执行逻辑
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

就拿上面这段代码来说,我们能不能不管start方法直接调用线程的run方法呢?

其实是可以调用run方法的,但是直接调用run方法只会在当前线程中运行run方法的代码,不会启动新的线程去执行run方法。

而调用start方法则会启动一个新的线程,然后在新的线程中执行run方法的代码。所以,如果只调用run方法而不调用start方法,则不会创建新的线程,无法实现多线程的并发执行。

也就是说,我们可以这样做,但是这样做的话就不会实现多线程,始终我们都只能在一个线程中运行。因此在实际开发中,并不建议这样做。 

还有一点需要注意的是,对于start方法,我们只能调用一次,不能重复调用,不然会报非法线程状态异常,因为在调用start方法后,线程就已经处于Runnable状态,对于已经是Runnable状态的线程,再让它start为Runnable状态显然是不合理的。

sleep() 与 yield() 

在多线程编程中,可以使用sleep和yield方法来控制线程的执行。

  • sleep方法:sleep方法是Thread类提供的静态方法,可以使当前线程暂停一段时间,让其他线程有机会执行。调用sleep方法后,线程会进入阻塞状态,不会占用CPU资源。sleep方法的语法是:Thread.sleep(long millis),其中millis参数表示暂停的时间,以毫秒为单位。例如,Thread.sleep(100)表示暂停100毫秒。
  • yield方法:yield方法是Thread类提供的静态方法,可以使当前线程让出CPU资源,使其他同优先级的线程有机会执行。调用yield方法后,线程会进入就绪状态,让出CPU资源,但并不是完全放弃CPU资源,可能会立即重新获取CPU资源。yield方法的语法是:Thread.yield()。例如,Thread.yield()表示当前线程让出CPU资源,给其他同优先级的线程执行的机会。

总的来说,sleep方法是让当前线程暂停一段时间,不会占用CPU资源,适合用于控制线程执行的时间间隔。yield方法是让当前线程主动让出CPU资源,给其他同优先级的线程执行的机会,适合用于在多个线程之间平衡负载,提高系统的性能。

join() 

join()方法是Thread类的一个方法,它用于等待该线程完成执行。具体而言,当调用一个线程的join()方法时,当前线程会被阻塞,直到该线程执行完成。

join()方法有两个重载版本:

  1. join():等待被调用线程执行完成。
  2. join(long millis):等待被调用线程执行完成,但最多等待millis毫秒。

下面是一个例子,演示如何使用join()方法等待线程执行完成:

public class JoinExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable(), "Thread 1");
        Thread thread2 = new Thread(new MyRunnable(), "Thread 2");

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("All threads have finished execution.");
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is executing.");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " has finished execution.");
    }
}

在上面的例子中,我们创建了两个线程thread1和thread2,并将它们启动。然后,我们使用join()方法等待这两个线程执行完成。最后,当两个线程都执行完成后,才会打印"All threads have finished execution."。

inerrupt() 

在Java中,线程的interrupt()方法用于中断线程。当一个线程调用interrupt()方法时,如果目标线程当前正在执行可中断的操作(如sleep()、join()、wait()等),它将会收到一个InterruptedException异常,从而提前退出。

如果目标线程没有在可中断操作中阻塞,而是在运行中,那么调用interrupt()方法将设置目标线程的中断标志位为true。这样,目标线程可以通过检查自己的中断标志位来自行决定是否中断执行。

下面是一个示例:

public class MyThread extends Thread {
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            // 执行一些操作
        }
        System.out.println("线程被中断");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        thread.interrupt();
    }
}

在上述示例中,我们创建了一个继承自Thread类的自定义线程MyThread。在run() 方法中,我们使用了一个循环来模拟线程的执行操作。每次循环都会检查中断标志位,如果为true则退出循环并输出"线程被中断"。在前文的Thread类常见属性也说过了使用 isInterrupted() 方法就可以获取当前线程的中断标志位。

备注:和 isInterrupted() 相似的还有一个方法叫做 interrupt() 二者都能判断线程是否被打断,但是不同的点在于前者只是做出判断,并不会手动修改这个标记;而后者会在判断后手动清除打断标记,也就是置为false。

在main方法中,我们创建了一个MyThread对象并启动线程。然后通过调用Thread.sleep() 方法来让主线程睡眠1秒,最后调用thread.interrupt() 方法中断线程。当调用interrupt() 方法时,MyThread线程在下一个循环迭代时会检查到中断标志位为true,从而退出了循环并输出"线程被中断"。




 本次的分享就到此为止了,希望我的分享能给您带来帮助,创作不易也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见

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

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

相关文章

sqlmodel实现唯一性校验3,检查多列同时重复

之前的方案虽然能够解决重复性问题,但是没有覆盖到多列同时重复的情况。 比如,我们可以认为用户名是可以重复的。但是用户名和年龄不能同时重复,那么这种情况该怎么解决呢? 之前的代码如下: from sqlalchemy import…

低优先级线程可能会使用 100% CPU

经常会有人问我这样的问题: 我将一个线程设置为低优先级,当我的应用程序运行起来之后,我在任务管理器中看到应用程序占满了接近 100% 的 CPU,这就有点奇怪了,难道低优先级设定没有起作用? 我想指出的是&a…

Pytorch入门实战 P09-YOLOv5里面的Backbone模块搭建网络

目录 1、YOLOv5的模型图。 2、BackBone简单介绍。 3、YOLOv5的Backbone文件。 4、YOLOv5Backbone的code部分 5、完整的code部分 6、结果展示 (1)Adam优化器 (2)SGD优化器 🍨 本文为🔗365天深度学习…

羊大师解析,鲜为人知的羊奶冷知识

羊大师解析,鲜为人知的羊奶冷知识 羊奶的脂肪球更小:相较于牛奶,羊奶中的脂肪球直径更小,这有助于其更快地被人体消化和吸收。 羊奶含有更多的中链脂肪酸:羊奶中含有较多的中链脂肪酸(MCT)&am…

WiFine通信与Wi-sun通信对比

调制速率 WiFine通信:(G)FSK 50Kbps~500Kbps ;LoRa 5Kbps~37.5Kbps Wi-Sun通信:(G)FSK 50Kbps~300Kbps ;QPSK/OFDM 计划中… 2、协议简介 WiFine通信:为低成本、低功耗、移动设备倾力打造 的轻量级、分布式无线移动…

Stateflow基础知识笔记

01--Simulink/Stateflow概述 Stateflow是集成于Simulink中的图形化设计与开发工具,主要 用于针对控制系统中的复杂控制逻辑进行建模与仿真,或者说, Stateflow适用于针对事件响应系统进行建模与仿真。 Stateflow必须与Simulink联合使用&#…

阿赵UE引擎C++编程学习笔记——HelloWorld

大家好,我是阿赵。   从这一篇开始学习虚幻引擎的C编程。   学习所有编程好像都应该从HelloWorld开始,所以我这里也不例外。不过为了能打印出HelloWorld,需要做的事情还不少。不过到了能打印出HelloWorld的时候,我感觉学习的过…

使用DxTex.exe工具处理DDS图片

一.DxTex.exe DDS文件:基于Direct3D引擎的纹理图片格式。 DxTex.exe:基于Direct3D引擎开发的DDS文件处理工具,文件分辨率修改,文件minmap修改。 DxTex.exe路径:Utilities\bin\x64\DxTex.exe 二.修改分辨率 Format-Resize Texture 三.修改mipmap Format-Change Surface Format

专业的保密网文件导入导出系统,让文件流转行为更可控安全

军工单位因其涉及国防安全和军事机密,对保密工作有极高的要求,通常会采取严格的网络隔离措施来保护敏感信息和提高网络安全性。常见的方式是通过物理隔离将网络彻底分隔开来,比如保密网和非保密网。网络隔离后,仍有数据交换的需求…

VALSE 2024年度进展评述内容分享-世界模型增强下的自动驾驶

2024年视觉与学习青年学者研讨会(VALSE 2024)于5月5日到7日在重庆悦来国际会议中心举行。本公众号将全方位地对会议的热点进行报道,方便广大读者跟踪和了解人工智能的前沿理论和技术。欢迎广大读者对文章进行关注、阅读和转发。文章是对报告人…

【kali换源之后签名无效,报错处理】

#一、问题:报错信息# 错误:1 http://mirrors.ustc.edu.cn/kali kali-rolling InRelease 错误:2 http://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling InRelease 错误:3 http://dl.google.com/linux/chrome/deb stable InRelease 错误:4 http://mirrors.aliyu…

LeetCode509:斐波那契数(使用动态规划)

题目描述 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) 0,F(1) 1 F(n) F(n - 1) F(n - 2),其中 n > 1…

Web3 ETF软件系统的主要功能

下面是Web3 ETF系统软件的主要功能,这些功能共同构成了Web3 ETF系统软件的核心,使其能够有效地为投资者提供Web3技术相关的投资机会,同时确保合规性、安全性和透明度。北京木奇移动软件有限公司,专业的软件外包开发公司&#xff0…

Java性能优化(一):ArrayList还是LinkedList?

引言 集合作为一种存储数据的容器,是我们日常开发中使用最频繁的对象类型之一。JDK为开发者提供了一系列的集合类型,这些集合类型使用不同的数据结构来实现。因此,不同的集合类型,使用场景也不同。 很多同学在面试的时候&#x…

3个电脑录制视频技巧,新手也能轻松上手

在当今信息化时代,电脑录制视频已经成为人们日常工作和学习中的一项重要技能。无论是录制在线会议、教程讲解还是游戏直播,一款合适的录屏软件都至关重要。本文将介绍三种常见的电脑录制视频方法,为广大用户提供详细的操作步骤,满…

Python中的分布式爬虫系统Scrapy与分布式任务队列的结合

随着互联网的不断发展,网络爬虫在数据采集和信息挖掘中发挥着重要作用。然而,单机爬虫往往难以应对大规模数据抓取的需求,因此,构建分布式爬虫系统成为了一种必然选择。本文将介绍如何利用 Python 中的 Scrapy 框架和分布式任务队…

VALSE 2024主旨报告内容解析:以深度学习框架为牵引促进自主AI生态发展

2024年视觉与学习青年学者研讨会(VALSE 2024)于5月5日到7日在重庆悦来国际会议中心举行。本公众号将全方位地对会议的热点进行报道,方便广大读者跟踪和了解人工智能的前沿理论和技术。欢迎广大读者对文章进行关注、阅读和转发。文章是对报告人…

AngusTester安装Mock服务

一、介绍 Mock服务(AngusMockService) 提供了一个虚拟环境,让您能够模拟不同的接口响应、状态码和数据,快速生成并模拟您所依赖的API,使开发和测试先行,以实现更快的开发和更全面的测试,更早地交付稳定的产品或应用。…

Amazon Bedrock 托管 Llama 3 8B70B

Amazon Bedrock 托管 Llama 3 8B&70B,先来体验:(*实验环境账号有效期为1天,到期自动关停,请注意重要数据保护) https://dev.amazoncloud.cn/experience/cloudlab?id65fd86c7ca2a0d291be26068&visi…

AI烟雾监测识别摄像机:智能化安全防范的新利器

随着现代社会的不断发展,人们对于安全问题的关注日益增加,尤其是在日常生活和工作中,对火灾等意外事件的预防成为了一项重要任务。为了更好地应对火灾风险,近年来,AI烟雾监测识别摄像机应运而生,成为智能化…