多线程篇-5--线程分类(线程类型,springboot中常见线程类型,异步任务线程)

news2024/12/1 18:29:12

常见的线程类型包括用户线程(User Threads)、守护线程(Daemon Threads)、主线程(Main Thread)、工作线程(Worker Threads)和线程池中的线程。

一、用户线程(User Threads)

特点:

  • 用户线程是普通的Java线程,通常由程序员显式创建。
  • 用户线程在程序运行期间一直存在,直到它们完成任务或程序终止。

代码示例:

public class UserThreadExample {
    public static void main(String[] args) {
        Thread userThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("User thread running: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        userThread.start();
    }
}

2、守护线程(Daemon Threads)

特点:

  • 守护线程是在后台运行的线程,它们通常用于为其他线程提供服务。
  • 当所有用户线程都结束时,JVM会自动终止守护线程并退出。(即使守护线程没有执行完任务,也会被JVM强制退出,所以主要业务一定不能放在守护线程中执行)
  • 守护线程通常用于执行一些辅助任务,如垃圾回收、日志记录等。

代码示例:

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

        daemonThread.setDaemon(true);  // 设置为守护线程,就这一句就够了
        daemonThread.start();

        // 主线程休眠一段时间后结束
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

解释:

  • 创建一个新的线程daemonThread,并将其设置为守护线程。
  • 守护线程无限循环运行,每秒打印一条消息。
  • 主线程休眠5秒后结束,此时JVM会自动终止,守护线程也会被终止。

3、主线程(Main Thread)

特点:

  • 主线程是每个Java应用程序的入口点,它是JVM在启动时自动创建的第一个线程。
  • 主线程负责执行main方法中的代码。
  • 当主线程结束时,如果还有其他用户线程在运行,JVM会继续运行这些线程。

代码示例:

public class MainThreadExample {
    public static void main(String[] args) {
        System.out.println("Main thread started");

        Thread userThread = new Thread(() -> {
            System.out.println("User thread running...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        userThread.start();  // 用户线程启动,实际上就是主线程干的事情
        System.out.println("Main thread ending");
    }
}

解释:

  • 主线程执行main方法中的代码。
  • 创建并启动一个用户线程userThread。
  • 主线程在启动用户线程后继续执行打印,之后就结束了。
  • 用户线程会继续运行,直到完成任务。

4、工作线程(Worker Threads)

特点:

  • 工作线程通常用于执行具体的任务,它们可以是用户线程或守护线程。
  • 工作线程通常由线程池管理,用于处理任务队列中的任务。
  • 工作线程可以提高应用程序的并发性和性能。

代码示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WorkerThreadExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);  // 创建固定大小的线程池

        for (int i = 0; i < 5; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();  // 关闭线程池
    }
}

解释:

  • 创建一个固定大小为2的线程池executor。
  • 提交5个任务到线程池,每个任务在执行时会打印任务ID和当前线程名称。
  • 调用shutdown方法关闭线程池,等待所有任务完成。

5、线程池中的线程

特点:

  • 线程池中的线程是由线程池管理的,它们可以重复使用,减少了创建和销毁线程的开销。
  • 线程池可以控制并发线程的数量,避免过多的线程消耗系统资源。
  • 线程池通常用于处理大量的短期任务。

代码示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();  // 创建可缓存线程池

        for (int i = 0; i < 10; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();  // 关闭线程池
    }
}

解释:

  • 创建一个可缓存线程池executor,它会根据需要创建新的线程。
  • 提交10个任务到线程池,每个任务在执行时会打印任务ID和当前线程名称。
  • 调用shutdown方法关闭线程池,等待所有任务完成。

6、线程分类总结

(1)、用户线程:普通的Java线程,由程序员显式创建。
(2)、守护线程:后台线程,用于提供服务,当所有用户线程结束时自动终止。
(3)、主线程:JVM自动创建的第一个线程,执行main方法中的代码。
(4)、工作线程:用于执行具体任务的线程,通常由线程池管理。
(5)、线程池中的线程:由线程池管理的线程,可以重复使用,减少开销。

7、springboot常见线程类型

(1)、主线程(Main Thread)

1、概述

  • 这是Spring Boot应用启动时由JVM创建的第一个线程。
  • 主线程负责启动Spring Boot应用,加载配置,初始化Spring容器等。

2、创建时机

  • 应用启动时,由JVM自动创建。

代码示例

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

(2)、Tomcat/NIO线程(接受客户端请求用)

1、概述

  • 如果你的Spring Boot应用使用了嵌入式的Tomcat服务器,Tomcat会创建多个线程来处理HTTP请求。
  • 这些线程通常是NIO线程(非阻塞线程,可异步),负责接收客户端请求并处理响应。

2、创建时机

  • 当Spring Boot应用启动并初始化嵌入式Tomcat服务器时。

配置文件示例
无需编码,仅配置即可,不配置也可以会有默认配置。

server:
  port: 8080
  tomcat:
    max-threads: 200  # 最大线程数
    min-spare-threads: 10  # 最小空闲线程数

(3)、定时任务线程(Scheduled Tasks)

1、概述

  • 如果你在Spring Boot应用中使用了定时任务(如@Scheduled注解),Spring Boot会创建一个或多个线程来执行这些定时任务。
  • 这些线程通常由TaskScheduler管理。

2、创建时机

  • 当Spring Boot应用启动并初始化定时任务时。

代码示例

@Configuration
@EnableScheduling
public class SchedulerConfig {
}

@Component
public class ScheduledTasks {

    @Scheduled(fixedRate = 5000)   // 自动创建定时任务线程监听并执行
    public void performTask() {
        System.out.println("Executing scheduled task at " + LocalDateTime.now());
    }
}

(4)、异步任务线程(Async Tasks)

1、概述

  • 如果你在Spring Boot应用中使用了异步方法(如@Async注解),Spring Boot会创建一个或多个线程来执行这些异步任务。
  • 这些线程通常由TaskExecutor管理。

这个在附录中在详细说明。

(5)、自定义线程

1、概述

  • 你可以在Spring Boot应用中手动创建和管理线程,例如通过实现Runnable接口或继承Thread类。

2、创建时机

  • 在你需要的时候手动创建。

代码示例

@Component
public class CustomThreadExample {

    @PostConstruct
    public void startCustomThread() {
        Thread customThread = new Thread(() -> {
            while (true) {
                System.out.println("Custom thread running at " + LocalDateTime.now());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        customThread.start();
    }
}

附录:

异步任务线程(Async Tasks)

1、概述

  • 如果你在Spring Boot应用中使用了异步方法(如@Async注解),Spring Boot会创建一个或多个线程来执行这些异步任务,主线程不会阻塞会继续执行后面的代码。
  • 这些线程通常由TaskExecutor管理。
  • 使用异步任务,使用@EnableAsync开启后(主方法等配置入口),在需要的方法上添加@Async注解即可。
2、异步任务线程的作用
(1)、提高响应性
  • 异步任务线程可以在后台执行耗时的操作,而不阻塞主线程或请求处理线程。这样,主线程可以继续处理其他请求,提高系统的整体响应性。
(2)、优化资源利用
  • 对于耗时较长的任务,如果使用同步方式处理,可能会占用宝贵的线程资源,导致其他任务无法及时处理。异步任务线程可以将这些任务移到后台执行,释放主线程资源。
(3)、并发处理
  • 异步任务线程可以并行处理多个任务,提高系统的并发处理能力。这对于需要处理大量并发请求的应用尤其重要。
3、异步任务特点

(1)、异步任务线程:异步任务在单独的线程中执行,不会阻塞主线程。
(2)、立即返回:异步方法调用会立即返回一个CompletableFuture对象,主线程可以继续执行其他任务。
(3)、回调机制:使用thenAccept等方法注册回调函数,这些回调函数在异步任务完成时被调用(被线程池的其他线程调用),不会阻塞主线程。

4、常见的异步任务场景

(1)、邮件发送

  • 发送邮件是一个耗时操作,通常需要几秒钟甚至更长时间。使用异步任务线程可以在后台发送邮件,而不会阻塞主线程。

(2)、文件上传和下载

  • 文件上传和下载操作通常涉及大量的I/O操作,使用异步任务线程可以避免阻塞主线程,提高系统的响应性。

(3)、数据处理和计算

  • 对于复杂的数据处理和计算任务,使用异步任务线程可以将这些任务移到后台执行,释放主线程资源。

(4)日志记录

  • 日志记录操作通常需要写入磁盘,使用异步任务线程可以避免阻塞主线程,提高系统的性能。
5、异步任务线程与异步请求处理的区别

前者是优化请求响应速度,把不重要或耗时的事情放在后台慢慢执行,主线程可以继续干其他事情或直接返回的一种处理方式;后者是并行可同时接受客户端的多个请求,两者完全不是一回事,需要注意下。

(1)、异步任务线程
  • 用于处理耗时较长的后台任务。
  • 通常由开发人员手动创建和管理。
  • 使用@Async注解和TaskExecutor来实现。
    即:用于处理耗时较长的后台任务,提高系统的响应性和并发处理能力。
(2)、异步请求处理
  • 用于处理HTTP请求的异步响应。
  • 通常由Web框架(如Spring MVC,springboot)自动支持。
  • 使用CompletableFuture、DeferredResult等异步API来实现。
    即:用于处理HTTP请求的异步响应,通常由Web框架自动支持。
6、如何使用异步任务
(1)、@EnableAsync

@EnableAsync注解只需要在一个配置类上启用一次,即可在整个应用程序中启用异步任务的支持。即:开启后会告诉Spring框架扫描并管理带有@Async注解的方法。

代码示例:

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AsyncConfig {
    // 配置类可以包含其他Bean和配置
}
(2)、@Async

1、@Async注解用于标记那些需要异步执行的方法上。注意,方法必须在一个Spring管理的Bean中,否则不会扫描到。
2、@Async标记的方法将在异步任务线程中执行,不会阻塞当前线程,而是立即返回一个Future对象或CompletableFuture对象。(这个Future对象现在是无法获取结果的,只能通过定义回调的方式用于异步任务完成后再去通知调用的线程,当然如果无需等待结果的也可以直接忽略返回结果)。

(3)、异步线程池

默认情况下,Spring是使用一个简单的线程池来执行异步任务。你也可以自定义线程池注入spring容器中,以更好地控制异步任务的执行。

3.1、自定义线程池示例

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync       // 开启异步任务
public class AsyncConfig {

    @Bean                      // 注入线程池
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

3.2、控制器示例

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/send-email")
    public String sendEmail(@RequestParam String recipient, @RequestParam String message) {
        // 调用异步方法
        CompletableFuture<String> result = asyncService.sendEmail(recipient, message);
        // 处理异步结果
        result.thenAccept(response -> {   // 定义的回调方法,不会阻塞主线程,主线程会定义完成之后直接跳过该方法,往后执行。在异步任务完成后,真正执行回调方法代码的是线程池中的线程。
            System.out.println("Response: " + response);   // 异步任务完成后,由线程池中的线程执行,主线程不会执行。
        });
        return "Request accepted, email will be sent asynchronously";
    }
}

3.3、异步方法

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;

@Service
public class AsyncService {

    @Async
    public CompletableFuture<String> sendEmail(String recipient, String message) {
        // 模拟耗时操作
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 发送邮件
        System.out.println("Email sent to " + recipient + ": " + message);
        return CompletableFuture.completedFuture("Email sent successfully");
    }
}

解释一下:
1、异步方法的调用:

  • AsyncControllersendEmail方法中,调用asyncService.sendEmail方法。
  • sendEmail方法被标记为@Async,因此它会在一个异步任务线程中执行。
  • 方法调用会立即返回一个CompletableFuture<String>对象,标识异步任务的结果对象。(这个对象现在是无法获取执行结果的,因为还没有执行完成,需要想要调用线程得到响应,可以通过定义回调的方式在异步任务完成后再通知调用的线程)。
    2、处理异步结果:
  • 使用result.thenAccept(response -> { ... })方法来处理异步任务的结果。
  • thenAccept方法注册一个回调函数,当异步任务完成时,回调函数会被调用,并传入任务的结果。
  • 这个回调函数在异步任务线程中执行,不会阻塞主线程。
    3、立即返回响应:
  • AsyncControllersendEmail方法在调用异步方法后立即返回一个字符串"Request accepted, email will be sent asynchronously"
  • 这个响应告诉客户端请求已经被接受,邮件将在后台异步发送。

学海无涯苦作舟!!!

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

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

相关文章

docker 在ubuntu系统安装,以及常用命令,配置阿里云镜像仓库,搭建本地仓库等

1.docker安装 1.1 先检查ubuntu系统有没有安装过docker 使用 docker -v 命令 如果有请先卸载旧版本&#xff0c;如果没有直接安装命令如下&#xff1a; 1.1.0 首先&#xff0c;确保你的系统包是最新的&#xff1a; 如果是root 权限下面命令的sudo可以去掉 sudo apt-get upda…

IOS ARKit进行图像识别

先讲一下基础控涧&#xff0c;资源的话可以留言&#xff0c;抽空我把它传到GitHub上&#xff0c;这里没写收积分&#xff0c;竟然充值才能下载&#xff0c;我下载也要充值&#xff0c;牛&#xff01; ARSCNView 可以理解画布或者场景 1 配置 ARWorldTrackingConfiguration AR追…

CSS笔记(二)类名复用

这里我通过两张不同位置的卡片来实现效果 代码 <!DOCTYPE html> <html><head><style>/*设置画布*/body{/* 方便排列与对齐*/display: flex; /*画布布满整个窗口*/height: 100vh;/*水平居中*/justify-content: center;/*垂直居中*/align-items: cente…

远程协助软件Todesk免费版有什么限制

大名鼎鼎的远程todesk也开始出限制了&#xff0c;国内远程协助一直是向日葵一家独大&#xff0c;todesk起来以后慢慢占领了部分市场&#xff0c;随用户越来越多&#xff0c;其服务器也开始不堪重负了&#xff0c;于2024年的6月发了公告&#xff0c;出告了限制发表的措施具体如下…

【力扣热题100】[Java版] 刷题笔记-3. 无重复字符的最长子串

题目:3. 无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 解题思路 根据题目&#xff0c;只需要返回无重复字符串的最长子串的长度&#xff0c;所以我们不需要知道知道字符串内容是什么&#xff0c;在整个字符串 s 中&…

【03】Selenium+Python 八种定位元素方法

操作元素&#xff0c;需要先查找定位到对应的元素。 查找单个元素&#xff1a;driver.find_element() 返回是一个web element 对象 查找多个元素&#xff1a;driver.find_elements() 返回是一个list对象 By 是 Selenium 中一个非常重要的类&#xff0c;用于定位网页元素。 使…

【文献阅读】自动化构音障碍严重程度分类:声学特征与深度学习技术的研究

自动化构音障碍严重程度分类:声学特征与深度学习技术的研究 文章目录 自动化构音障碍严重程度分类:声学特征与深度学习技术的研究思维导图摘要I. 引言A. 动机与相关工作II. 数据库III. 实验设计A. 分析 MFCC 和 CQCCB. 分析语言障碍特定特征C. 分析 i-向量IV. 特征设计V. 分类…

设计模式 外观模式 门面模式

结构性模式-外观模式 门面模式 适用场景&#xff1a;如果你需要一个指向复杂子系统的直接接口&#xff0c; 且该接口的功能有限&#xff0c; 则可以使用外观模式。 不用关心后面的查询具体操作 /*** 聚合查询接口*/ RestController RequestMapping("/search") Slf…

【DB】根据某字段生成序号

如下图所示&#xff1a;根据RCV_ORD_NUM,生成明细行号&#xff0c;当RCV_ORD_NUM相同时&#xff0c;序号自增&#xff0c;不同时&#xff0c;从1开始并始终保持四位字符串 直接更新的方式 SET i : 0, now_region_id : NULL; UPDATE tqt_pl_prod.t_receipt_order_line l1 JOIN …

STM32的CAN波特率计算

公式&#xff1a; CAN波特率 APB总线频率 / &#xff08;BRP分频器 1&#xff09;/ (SWJ BS1 BS2) SWJ一般为1。 例如STM32F407的&#xff0c;CAN1和CAN2都在在APB1下&#xff0c;频率是42000000 如果想配置成1M波特率&#xff0c;则计算公式为&#xff1a;

【算法day4】链表:应用拓展与快慢指针

题目引用 两两交换链表节点删除链表的倒数第n个节点链表相交环形链表 1.两两交换链表节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&am…

电子应用设计方案-33:智能AI投影仪系统方案设计

智能 AI 投影仪系统方案设计 一、引言 随着科技的不断进步&#xff0c;投影仪在家庭娱乐、商务办公和教育培训等领域的应用越来越广泛。智能 AI 投影仪作为一种创新的投影设备&#xff0c;结合了人工智能技术&#xff0c;为用户带来更便捷、智能和个性化的使用体验。 二、系统…

Python语法基础(五)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 首先&#xff0c;我们需要明白一句话&#xff0c;在Python中&#xff0c;一切都是对象 单片机&#xff0c;嵌入式都是面向过程的 面向对象有封装&#xff0c;继承&#xff0c;多态…

pip install causal-conv1d==1.1.1报错

Building wheels for collected packages: causal-conv1d Building wheel for causal-conv1d (setup.py) ... error error: subprocess-exited-with-error python setup.py bdist_wheel did not run successfully. │ exit code: 1 ╰─> [8 lines of output]…

Xcode15(iOS17.4)打包的项目在 iOS12 系统上启动崩溃

0x00 启动崩溃 崩溃日志&#xff0c;只有 2 行&#xff0c;看不出啥来。 0x01 默认配置 由于我开发时&#xff0c;使用的 Xcode 14.1&#xff0c;打包在另外一台电脑 Xcode 15.3 Xcode 14.1 Build Settings -> Asset Catalog Compliter - Options Xcode 15.3 Build S…

14 —— Webpack解析别名

import {checkPhone, checkCode} from ../src/utils/check.js 这么使用相对路径不安全 —— 在webpack.config.js中配置解析别名来代表src绝对路径

SystemUI 下拉框 Build 版本信息去掉

需求及场景 去掉SystemUI 下拉框 Build 版本信息 如下图所示&#xff1a;去掉 12 &#xff08;SP1A.201812.016) 了解 去掉之前我们先了解它是个什么东西:其实就是一个Build RTM 信息显示 Android_12_build_SP1A.210812.016 修改文件 /frameworks/base/packages/Syste…

基于协同推荐的黔醉酒业白酒销售系统

文末获取源码和万字论文 摘 要 基于协同推荐的黔醉酒业白酒销售系统主要针对黔醉酒业的具体业务需求所设计&#xff0c;现阶段阶段我国大型企业都会有自己的电商平台以及销售管理系统&#xff0c;其功能对于中小型过于冗长复杂&#xff0c;成本也不是中小型企业能够承受的&…

爬虫笔记24——纷玩岛自动抢票脚本笔记

纷玩岛自动抢票&#xff0c;协议抢票思路实现 一、获取Authorization凭证二、几个关键的参数三、几个关键的接口获取参数v&#xff0c;这个参数其实可以写死&#xff0c;可忽略通过价位获取演出的参数信息获取观演人信息&#xff0c;账号提前录入即可提交订单接口 先看实现图&a…

并发专题(7)之JUC并发工具源码分析

一、CountdownLatch源码分析 1.1 CountdownLatch应用 CountDownLatch本身就好像一个计数器&#xff0c;可以让一个线程或多个线程等待其他线程完成后再执行。 public static void main(String[] args) throws InterruptedException, BrokenBarrierException {// 声明CountDow…