[JAVA基础知识汇总-1] 创建线程的几种方式(含线程池相关)

news/2024/9/17 6:07:53/ 标签: java, 开发语言

文章目录

  • 1. 继承Thread类
  • 2. 实现Runnable接口
  • 3. 实现Callable接口
  • 4. 线程池
    • 4.1 利用Executors工具类来创建线程池
    • 4.2 为什么不建议使用Executors来创建线程池?
    • 4.3 ThreadPoolExecutor是线程池的核心实现类,可以利用它来创建线程池
    • 4.4 线程池的状态

可以认为有四种方式,也可以认为有一种,因为都跟Runnable接口有关

1. 继承Thread类

代码

java">public class Thread1ExtendsThread extends Thread {
//    public Thread1(String name) {
//        super(name);
//    }@Overridepublic void run() {for (int i = 0; i < 3; i++) {System.out.println("这里是自定义线程:" + Thread.currentThread().getName() + ", i = " + i + ";");}}public static void main(String[] args) {// new Thread1("xinliushijian").start();new Thread1ExtendsThread().start();for (int i = 0; i < 3; i++) {System.out.println("这里是main线程:" + Thread.currentThread().getName() + ", i = " + i + ";");}}
}

打印

这里是main线程:main, i = 0;
这里是main线程:main, i = 1;
这里是main线程:main, i = 2;
这里是自定义线程:Thread-0, i = 0;
这里是自定义线程:Thread-0, i = 1;
这里是自定义线程:Thread-0, i = 2;

知识点

  1. new Thread 构造器的参数可以是Runnable,但没有Callable,因为如果是实现了Callable接口的线程,参数应该是FutureTask
  2. Thread 是Runnable接口的实现类
  3. 这种方式的坏处是不能再继承其他类了,因为java是单继承
  4. 没有返回值

在这里插入图片描述

2. 实现Runnable接口

代码

java">public class Thread2Runnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 3; i++) {System.out.println("这里是自定义线程:" + Thread.currentThread().getName() + ", i = " + i + ";");}}public static void main(String[] args) {Thread2Runnable thread2 = new Thread2Runnable();new Thread(thread2).start();for (int i = 0; i < 3; i++) {System.out.println("这里是main线程:" + Thread.currentThread().getName() + ", i = " + i + ";");}}
}

打印

这里是main线程:main, i = 0;
这里是main线程:main, i = 1;
这里是main线程:main, i = 2;
这里是自定义线程:Thread-0, i = 0;
这里是自定义线程:Thread-0, i = 1;
这里是自定义线程:Thread-0, i = 2;

知识点

  1. 没有返回值

3. 实现Callable接口

代码

java">import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Thread3Callable implements Callable {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 3; i++) {sum += i;System.out.println("这里是自定义线程:" + Thread.currentThread().getName() + ", i = " + i + ";");}return sum;}public static void main(String[] args) {// 使用Lambda表达式创建Callable对象, FutureTask类来包装Callable对象
//        FutureTask<Integer> future = new FutureTask<>(
//                () -> 3
//        );FutureTask<Integer> future = new FutureTask<>(new Thread3Callable());// 实质上还是以Callable对象来创建并启动线程new Thread(future).start();try {// get()方法会阻塞,直到子线程执行结束才返回System.out.println("自定义线程返回值:" + future.get());} catch (Exception e) {e.printStackTrace();}for (int i = 0; i < 3; i++) {System.out.println("这里是main线程:" + Thread.currentThread().getName() + ", i = " + i + ";");}}
}

打印

这里是自定义线程:Thread-0, i = 0;
这里是自定义线程:Thread-0, i = 1;
这里是自定义线程:Thread-0, i = 2;
自定义线程返回值:3
这里是main线程:main, i = 0;
这里是main线程:main, i = 1;
这里是main线程:main, i = 2;

知识点

  1. 有返回值,需要跟FutureTask搭配使用,来获得线程的执行结果
  2. FutureTask也是实现了Runnable接口
  3. 以上三种方式都是new Thread().start()的方式去启动线程
    在这里插入图片描述

4. 线程池

4.1 利用Executors工具类来创建线程池

代码

java">import com.google.common.util.concurrent.ThreadFactoryBuilder;import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;public class ThreadDemo3Executors {public static void main(String[] args) {// 创建线程工厂ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Async-pool-%d").build();// 创建定时任务的线程池Executors.newScheduledThreadPool(4, threadFactory);// 创建可缓存的线程池,只会重用空闲可用的线程,没有可用的线程时会创建新线程Executors.newCachedThreadPool();// 单个线程的线程池Executors.newSingleThreadExecutor();// 固定线程数量的线程池Executors.newFixedThreadPool(3);}
}

4.2 为什么不建议使用Executors来创建线程池?

以Executors.newFixedThreadPool(3)来举例

源码

java">    public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}public LinkedBlockingQueue() {this(Integer.MAX_VALUE);}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();

知识点

我们发现其实他也是利用ThreadPoolExecutor来创建管理线程池,这种方式不是可以的吗!
问题是它没有定制化,
它只能控制核心线程数和最大线程数两个值,且它俩是相等的,其他的都是默认的。
阻塞队列是LinkedBlockingQueue,容量直接Integer最大值,其中可放21亿多的任务,若是真的任务巨多,会造成OOM

4.3 ThreadPoolExecutor是线程池的核心实现类,可以利用它来创建线程池

在这里插入图片描述

Executor线程池相关顶级接口,它将任务的提交与任务的执行分离开来
ExecutorService继承并扩展了Executor接口,提供了Runnable、FutureTask等主要线程实现接口扩展
ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务
ScheduledExecutorService继承ExecutorService接口,并定义延迟或定期执行的方法
ScheduledThreadPoolExecutor继承ThreadPoolExecutor并实现了ScheduledExecutorService接口,是延时执行类任务的主要实现

源码

java">    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}

ThreadPoolExecutor包含了7个核心参数,参数含义:

corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:当线程池中线程数大于corePoolSize,并且没有可执行任务时大于corePoolSize那部分线程的存活时间
unit:keepAliveTime的时间单位
workQueue:用来暂时保存任务的工作队列
threadFactory:线程工厂提供线程的创建方式,默认使用Executors.defaultThreadFactory()
handler:当线程池所处理的任务数超过其承载容量或关闭后继续有任务提交时,所调用的拒绝策略

代码

java">import com.google.common.util.concurrent.ThreadFactoryBuilder;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadDemo4ThreadPoolExecutor {public static final ThreadPoolExecutor EXECUTOR_SERVICE;static {int corePoolSize = 500;int maxPoolSize = 500;ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("xinliushijian-thread-pool-%d").build();RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {if (!executor.isShutdown()) {try {executor.getQueue().put(r);} catch (InterruptedException e) {System.out.println("hahaha");}}}};EXECUTOR_SERVICE = new ThreadPoolExecutor(corePoolSize, maxPoolSize, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue(), threadFactory, rejectedExecutionHandler);}public static void main(String[] args) {System.out.println(Thread.currentThread().getName());List<String> nameList = new ArrayList<>();List<String> nameResultList = new ArrayList<>();nameList.add("xiaohua");nameList.add("xiaohua1");nameList.add("xiaohua2");nameList.add("xiaohua3");nameList.add("xiaohua4");nameList.add("xiaohua5");nameList.add("xiaohua6");nameList.add("xiaohua7");CountDownLatch countDownLatch = new CountDownLatch(nameList.size());nameList.forEach(name -> {dealWithName(name, countDownLatch, nameResultList);});System.out.println("nameResultList前: " + nameResultList);try {countDownLatch.await();} catch (InterruptedException e) {System.out.println("exception");}System.out.println("nameResultList后: " + nameResultList);}private static void dealWithName(String name, CountDownLatch countDownLatch, List<String> nameResultList) {EXECUTOR_SERVICE.execute(() -> {countDownLatch.countDown();System.out.println(name + "haha");System.out.println(Thread.currentThread().getName());// 打印当前线程数System.out.println("打印当前线程数: " + EXECUTOR_SERVICE.getPoolSize());// 打印执行任务的线程数System.out.println("打印执行任务的线程数: " + EXECUTOR_SERVICE.getActiveCount());// 总任务数 = 正在执行的任务数 + 队列任务数System.out.println("总任务数 = 正在执行的任务数 + 队列任务数: " + EXECUTOR_SERVICE.getTaskCount());nameResultList.add(name);});}
}

打印

main
xiaohuahaha
xinliushijian-thread-pool-0
xiaohua1haha
xinliushijian-thread-pool-1
打印当前线程数: 3
xiaohua3haha
xinliushijian-thread-pool-3
xiaohua2haha
xinliushijian-thread-pool-2
打印当前线程数: 5
打印当前线程数: 4
打印执行任务的线程数: 6
打印当前线程数: 5
总任务数 = 正在执行的任务数 + 队列任务数: 6
xiaohua5haha
xinliushijian-thread-pool-5
打印当前线程数: 7
打印执行任务的线程数: 6
总任务数 = 正在执行的任务数 + 队列任务数: 8
xiaohua6haha
xinliushijian-thread-pool-6
打印当前线程数: 8
打印执行任务的线程数: 6
总任务数 = 正在执行的任务数 + 队列任务数: 8
打印执行任务的线程数: 5
xiaohua7haha
xinliushijian-thread-pool-7
打印当前线程数: 8
nameResultList前: [xiaohua2, xiaohua5]
打印执行任务的线程数: 6
总任务数 = 正在执行的任务数 + 队列任务数: 8
打印执行任务的线程数: 6
打印执行任务的线程数: 5
总任务数 = 正在执行的任务数 + 队列任务数: 8
总任务数 = 正在执行的任务数 + 队列任务数: 8
总任务数 = 正在执行的任务数 + 队列任务数: 8
xiaohua4haha
xinliushijian-thread-pool-4
nameResultList后: [xiaohua2, xiaohua5, xiaohua6, xiaohua3, xiaohua, xiaohua7, xiaohua1]
打印当前线程数: 8
打印执行任务的线程数: 1
总任务数 = 正在执行的任务数 + 队列任务数: 8

源码

java">ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("xinliushijian-thread-pool-%d").build();
public ThreadFactory build() {return doBuild(this);
}
private static ThreadFactory doBuild(ThreadFactoryBuilder builder) {final String nameFormat = builder.nameFormat;final Boolean daemon = builder.daemon;final Integer priority = builder.priority;final Thread.UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler;final ThreadFactory backingThreadFactory = builder.backingThreadFactory != null ? builder.backingThreadFactory : Executors.defaultThreadFactory();final AtomicLong count = nameFormat != null ? new AtomicLong(0L) : null;return new ThreadFactory() {public Thread newThread(Runnable runnable) {Thread thread = backingThreadFactory.newThread(runnable);Objects.requireNonNull(thread);if (nameFormat != null) {thread.setName(ThreadFactoryBuilder.format(nameFormat, ((AtomicLong)Objects.requireNonNull(count)).getAndIncrement()));}if (daemon != null) {thread.setDaemon(daemon);}if (priority != null) {thread.setPriority(priority);}if (uncaughtExceptionHandler != null) {thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);}return thread;}};}public interface ThreadFactory {/*** Constructs a new {@code Thread}.  Implementations may also initialize* priority, name, daemon status, {@code ThreadGroup}, etc.** @param r a runnable to be executed by new thread instance* @return constructed thread, or {@code null} if the request to*         create a thread is rejected*/Thread newThread(Runnable r);
}

知识点

从上面源码可以清楚看到,其中创建线程的步骤在ThreadFactory,线程工厂创建的线程是实现了Runnable接口,所以利用线程池来创建线程也是跟Runnable有关,所以这四种创建线程的方式其实都跟Runnable有关。

4.4 线程池的状态

在这里插入图片描述

线程池的5种状态

java">    private static final int COUNT_BITS = Integer.SIZE - 3;// runState is stored in the high-order bitsprivate static final int RUNNING    = -1 << COUNT_BITS;private static final int SHUTDOWN   =  0 << COUNT_BITS;private static final int STOP       =  1 << COUNT_BITS;private static final int TIDYING    =  2 << COUNT_BITS;private static final int TERMINATED =  3 << COUNT_BITS;
  • 1. RUNNING 运行状态
    线程池新建或调用execute()方法后,处于运行状态,能够接收新的任务

  • 2. SHUTDOWN 关闭状态
    调用shutdown()方法后的状态,此时线程池不再接收新的任务,但会继续处理已提交的任务队列中的任务

  • 3. STOP 停止状态
    调用shutdownNow()方法后的状态,此时线程池不再接收新的任务,不处理任务队列中的任务,并且中断正在进行的任务

  • 4. TIDYING 整理状态
    只是一个中间状态,不做任务处理

java">    final void tryTerminate() {for (;;) {int c = ctl.get();if (isRunning(c) ||runStateAtLeast(c, TIDYING) ||(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))return;if (workerCountOf(c) != 0) { // Eligible to terminateinterruptIdleWorkers(ONLY_ONE);return;}final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {try {terminated();} finally {ctl.set(ctlOf(TERMINATED, 0));termination.signalAll();}return;}} finally {mainLock.unlock();}// else retry on failed CAS}}/*** Method invoked when the Executor has terminated.  Default* implementation does nothing. Note: To properly nest multiple* overridings, subclasses should generally invoke* {@code super.terminated} within this method.*/protected void terminated() { }

从上面源码可以看到,此时所有任务都已终止,workerCount为0。

当线程池处于调整状态后执行了一个terminated()方法,而它是个空方法,所以调整状态默认是不会做任务动作的。

通过注释我们发现设计它的用处,它是一个可以被子类继承的protected修饰的方法,我们可以认为调整状态的作用就是方便子类继承扩展的,在任务都终止后做我们自定义的动作。

  • 5. TERMINATED 终止状态
    线程池内所有的线程都已终止时,就会进入终止状态

http://www.ppmy.cn/news/1522347.html

相关文章

Android 开发避坑经验第四篇:正确处理Activity和Fragment的状态保存与恢复

在 Android 开发中,​​Activity​​ 和 ​​Fragment​​ 的状态保存与恢复是一个常见的坑点。如果处理不当,可能会导致应用在屏幕旋转、后台恢复等场景下出现数据丢失、UI 状态不一致等问题。本篇文章将详细探讨如何正确保存和恢复 ​​Activity​​ 与 ​​Fragment​​ 的…

数学基础 -- 线性代数之LU分解

LU分解 LU分解&#xff08;LU Decomposition&#xff09;是线性代数中非常重要的一种矩阵分解方法。它将一个方阵分解为一个下三角矩阵&#xff08;L矩阵&#xff09;和一个上三角矩阵&#xff08;U矩阵&#xff09;的乘积。在数值线性代数中&#xff0c;LU分解广泛用于求解线…

培训第九周(部署k8s基础环境)

一、前期系统环境准备 1、关闭防火墙与selinux [rootk8s-master ~]# systemctl stop firewalld[rootk8s-master ~]# systemctl disable firewalldRemoved symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.Removed symlink /etc/systemd/system/dbus-o…

c++ 指针的用法详解

C中的指针是一个非常重要的概念&#xff0c;指针变量用于存储其他变量的内存地址。通过指针&#xff0c;程序可以直接访问和操作内存&#xff0c;提高了程序的灵活性和效率。以下是关于C指针用法的详解。 1. 指针的基本概念 定义&#xff1a;指针是一个变量&#xff0c;其值为…

思科网络地址转换5

#网络安全技术实现# #任务五利用动态NAPT实现局域网访问Internet5# #1配置计算机的IP 地址、子网掩码和网关 #2配置路由器A的主机名称及其接口IP地址 Router>enable Router#conf t Router(config)#hostname Router-A Router-A(config)#int f0/0 Router-A(config-if)#i…

Notepad++ 下载安装教程

目录 1.下教程 2.安装教程 1.下教程 Downloads | Notepad (notepad-plus-plus.org) 进入下载地址后选择最新版点击连接 点击链接后&#xff0c;向下滑动&#xff0c;下载适合自己电脑版本的安装包 这里大家没有梯子可能打不开页面&#xff0c;可以直接从本文开头下载。 2.安…

Spring Boot之数据访问集成入门

Spring Boot中的数据访问和集成支持功能是其核心功能之一&#xff0c;通过提供大量的自动配置和依赖管理&#xff0c;极大地简化了数据访问层的开发。Spring Boot支持多种数据库&#xff0c;包括关系型数据库&#xff08;如MySQL、Oracle等&#xff09;和非关系型数据库&#x…

ffmpeg 视频编码及基本知识

理论 H264编码原理&#xff08;简略&#xff09; 1. 视频为什么需要进行编码压缩 降低视频数据大小&#xff0c;方便存储和传输 2. 为什么压缩的原始数据采用YUV格式 彩色图像的格式是 RGB 的&#xff0c;但RGB 三个颜色是有相关性的。 采用YUV格式&#xff0c;利用人对图像的…

redis之zset命令学习

redis之zset命令学习 zset是一个不包含重复元素的字符串集合&#xff0c;且每个元素都会关联一个 double 类型的分数&#xff08;score&#xff09;。这使得有序集合既可以通过成员&#xff08;member&#xff09;来查询&#xff0c;也可以通过分数&#xff08;score&#xff…

ctfshow-php特性(web123-web150plus)

​web123 <?php error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a$_SERVER[argv]; $c$_POST[fun]; if(isset($_POST[CTF_SHOW])&&isset($_POST[CTF_SHOW.COM])&&!isset($_GET[fl0g])){if(!preg_match("/\\\\|\/|\~|…

彻底解决win10系统Tomcat10控制台输出中文乱码

彻底解决Tomcat10控制台输出中文乱码 首先乱码问题的原因通俗的讲就是读的编码格式和写的解码格式不一致&#xff0c;比如最常见的两种中文编码UTF-8和GBK,UTF-8一个汉字占三个字节&#xff0c;GBK一个汉字占两个字节&#xff0c;所以当编码与解码格式不一致时&#xff0c;输出…

封装触底加载组件

&#xff08;1&#xff09;首先创建一个文件名为&#xff1a;InfiniteScroll.vue <template><div ref"scrollContainer" class"infinite-scroll-container"><slot></slot><div v-if"loading" class"loading-sp…

如何完成本科毕业论文设计

完成本科毕业论文设计是一个系统性的工程&#xff0c;需要经过多个阶段的规划、执行和总结。以下是一个详细的步骤指南&#xff0c;帮助你顺利完成本科毕业论文设计。 ### 1. 选题与开题 - **选题**&#xff1a;选择一个有研究价值且你感兴趣的题目。与导师讨论&#xff0c;确…

CSS瀑布流实现

文章目录 前言前置知识 React 中实现代码实现 Vue 中实现代码实现 前言 瀑布流是一种CSS布局技术&#xff0c;它允许不同高度的元素在页面上以美观的方式排列&#xff0c;同时保持行与列间的间距一致。 前置知识 使用 multi-column 实现多列布局 column-count: 设置布局显示…

MongoDB 的适用场景

MongoDB 的适用场景 MongoDB 是一种基于文档存储的 NoSQL 数据库&#xff0c;与传统的关系型数据库不同&#xff0c;它使用 JSON 类似的二进制文档格式&#xff08;BSON&#xff09;来存储数据&#xff0c;并且具备灵活的文档模型、强大的查询能力和水平扩展性。这些特性使得 …

音乐项目

获取验证码&#xff1a; 将获取验证码的消息发送给前端&#xff0c;再由后端发给前端 function getverification_code(event) {event.preventDefault();console.log(点击获取验证码按钮);// 获取输入元素的值const emailInput document.getElementById(email);const emailVal…

Leetcode JAVA刷刷站(112)路径总和

一、题目概述 二、思路方向 为了解决这个问题&#xff0c;我们可以使用深度优先搜索&#xff08;DFS&#xff09;算法来遍历二叉树&#xff0c;并检查从根节点到叶子节点的路径和是否等于目标和。 三、代码实现 class TreeNode { int val; TreeNode left; TreeNode rig…

架构全景视图

文章目录 一、战略规划二、业务架构Business Architecture2.1业务架构定义2.2 业务架构组成2.3 TOGAF2.3.1 Archimate建模&#xff08;重要&#xff09; 三、数据架构Data Architecture3.1 数据架构定义3.2 数据架构组成 四、应用架构Application Architecture4.1 应用架构定义…

相亲交友系统商业开发

在快节奏的现代生活中&#xff0c;寻找真爱成为了许多人的渴望。相亲交友系统&#xff0c;作为连接心灵的桥梁&#xff0c;正逐渐成为人们寻找伴侣的首选方式。我们的团队h17711347205致力于开发一款创新的相亲交友系统&#xff0c;旨在通过智能化的匹配算法&#xff0c;为用户…

The Prompt Report 2

The Prompt Report 提示工程调查报告《The Prompt Report: A Systematic Survey of Prompting Techniques》 主要内容 Core Prompting Techniques Text based Techniques&#xff1a;PRISMA流程&#xff0c;58中基于文本的提示技术&#xff0c;提示语术语分类表&#xff1b;M…