• 最全面的创建线程的几种方式总结,让你的需求轻松选择最合适你的线程创建方式


    在java进程中,每一个线程都对应着一个Thread实例,线程的描述信息都会保存在Thread实例属性中, 供JVM进行线程管理和调度时使用功能.Thread 类是java多线程编程的基础, 创建线程有以下4中方式:

    • 继承Thread类创建线程类
    • 实现Runnable接口创建线程目标类
    • 使用Callable 和FutureTask 创建线程
    • 通过线程池创建线程

    继承Thread类创建线程类

    继承Thread类创建线程类是java多线程编程的最基础的创建方式,继承Thread类的同时需要重写run()方法,将需要并发执行的业务代码编写在run()方法中,示例代码如下:

    package com.th.thread;
    
    /**
     * @ClassName: CreateDemo
     * @Description:
     * @Author: 唐欢
     * @Date: 2022/8/11 15:24
     * @Version 1.0
     */
    public class CreateDemo {
        public  static final int MAX_TURE=5;
    
        public  static String getCurThreadName(){
            return  Thread.currentThread().getName();
        }
    
        //线程的编号
        static int threadNo=1;
    
        static class  DemoThread extends  Thread{
            public  DemoThread(){
                super("DemoThrea-"+threadNo++);
            }
            @Override
            public  void run(){
                for (int i=1;i
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    运行结果如下:
    在这里插入图片描述
    从代码中可以看到,设计了一个静态内部类DemoThread,方便防伪外部类的成员属性和方法,静态内部类DemoThread 重写了Thread类的run()方法,并将需要并发执行的用户业务代码编写在重写的run()方法中.

    在创建线程的时候继承Thread方法需要重写run()方法, 接下来我们就来看下Thread.run()方法的实现代码:
    在这里插入图片描述
    从代码中可看到,如果target(执行目标)不为空,就执行target属性的run方法.target 属性的定义如下:
    在这里插入图片描述
    target 属性是Thread 属性是Thread 类的一个实例属性,并且target属性的类型为Runable.

    实现Runnable接口创建线程目标类

    Runnable 是一个极为简单的接口,代码如下:
    在这里插入图片描述
    Runnable接口有且仅有一个抽象方法 run(),并被用注解@FunctionalInterface 标注为函数式接口. 在使用的时候,将用户业务逻辑编写在run()方法中. 当Runnable 实例传入Thread 实例的target属性后,Runnable 接口的run()的实现版本被异步调用.

    我们先来看一个通过实现Runnable接口创建线程的示例:

    package com.th.runnable;
    
    import com.th.util.ThreadUtil;
    
    /**
     1. @ClassName: CreateDemo
     2. @Description:
     3. @Author: 唐欢
     4. @Date: 2022/8/11 15:35
     5. @Version 1.0
     */
    public class CreateDemo {
    
        public  static  final int MAX_TURN=5;
    
        static int threadNo=1;
    
        static  class RunTarget implements  Runnable{
    
            @Override
            public void run() {
                for (int j=1 ;j
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    从示例代码中可看到,静态内部类RunTarget 执行目标类实现Runnable接口需要一部并发执行代码逻辑被编写在它的run()方法中.将Runnable 实例作为target 执行目标传入Thread实例. 具体实现步骤如下:
    (1) 定义一个新类实现Runable接口;

    (2) 实现runnable接口中的run()抽象方法,将线程代码逻辑存放在该run()实现版本中.

    (3) 通过Thread类创建线程对象, 将Runnable 实例作为实际参数传递给Thread类的构造器,由Thread 构造器将该Runnable 实例赋值给自己的target 执行目标属性.

    (4) 调用Thread实例的start()方法启动线程.

    (5)线程启动之后,线程的run()方法将被JVM 执行,该run()方法将调用target 属性的run()方法,从而完成Runnable实现类中业务代码逻辑的并发执行.

    使用Runnable 创建线程目标类除了直接实现Runnable 接口之外,还有两种比较优雅的实现方式,

    通过匿名类创建Runnable线程目标类

    在实现Runnable 接口创建线程目标类时,若target 实现类是一次性的,就可以使用匿名类实现,实现代码如下:

    package com.th.runnable;
    
    import com.th.util.ThreadUtil;
    
    import java.time.chrono.ThaiBuddhistEra;
    
    /**
     * @ClassName: CreateDemo2
     * @Description: 通过匿名类优雅地创建Runnable 线程目标类
     * @Author: 唐欢
     * @Date: 2022/8/11 16:21
     * @Version 1.0
     */
    public class CreateDemo2 {
        public  static final int MAX_TURN=5;
    
        static int threadNo= 1;
    
        public static void main(String[] args) {
            Thread thread =null;
    
            //使用Runnable的匿名类创建和启动线程
            for (int i=0;i<2 ;i++){
                thread =new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j=1;j

使用Lambda 表达式创建Runnable线程目标类.

通过Lambda 表达式直接编写Runnable 接口的run()方法的实现代码,接口名称(Runnable) ,方法名称run() 统统都被省略,仅剩下了run()方法的形参列表和方法体,示例代码如下:

package com.th.runnable;

import com.th.util.ThreadUtil;

import java.awt.image.ShortLookupTable;

/**
 * @ClassName: CreateLambdaDemo
 * @Description:
 * @Author: 唐欢
 * @Date: 2022/8/11 16:33
 * @Version 1.0
 */
public class CreateLambdaDemo {
    public  static final  int MAX_TURN =5;
    static int threadNo=1;

    public static void main(String[] args) {
        Thread thread =null;

        //使用Lambda 表达式形式创建和启动线程
        for (int i =0 ;i<2;i++){
            thread =new Thread(()->{
                for (int j=1;j

使用Callable 和FutureTask 创建线程

虽然说是使用Thread和Runnable可以创建线程,但是这两中方法有一个共同的缺点:不能获取异步执行的结果.

在jdk1.5 后,提供了一种新的多线程创建方法,通过Callable接口和FutureTask类相结合创建线程.

通过Callable接口和Future接口相结合创建多线程能够获取异步执行结果,创建线程的步骤如下:
(1) 创建一个Callable 接口的实现类,并实现其Call()方法,编写好异步执行的具体逻辑,可以有返回值,

(2) 使用Callable实现类的实例构造一个FutureTask 实例.\

(3) 使用FutureTask实例作为Thread构造器的target入参,构造新的Thread 线程示例.

(4)调用Thread 实例的start()方法启动新线程,启动新线程的run()方法并发执行.其内部的执行过程为:启动Thread实例的run()方法并发执行后,会执行FutureTask实例的run()方法,最终会并发执行Callable实现类的call()方法.

(5)调用FutureTask 对象的get() 方法阻塞性地获得并发线程的执行结果.

示例的代码如下:

package com.th.callableandfuture;

import com.th.util.ThreadUtil;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @ClassName: CreateDemo
 * @Description:
 * @Author: 唐欢
 * @Date: 2022/8/12 13:48
 * @Version 1.0
 */
public class CreateDemo {

    public static  final int MAX_TURE=5;

    public  static final int COMPUTE_TIME=100000000;

    /**
     * ① 创建一个Callable 接口的实现类
     */
    static  class ReturnalbleTask implements Callable{
            //② 编写好异步执行的具体逻辑,可以有返回值

        @Override
        public Long call() throws Exception {
           Long startTime = System.currentTimeMillis();
            System.out.println(ThreadUtil.getCurThreadName() +"线程运行开始,");
            Thread.sleep(1000);
            for (int i=0;i futureTask = new FutureTask(task);
        Thread thread = new Thread(futureTask,"returnableThread");

        thread.start();
        Thread.sleep(500);
        System.out.println(ThreadUtil.getCurThreadName()+"让子弹飞一会儿,");
        System.out.println(ThreadUtil.getCurThreadName()+"做一点自己的事情");
        for (int i=0;i

从示例代码中可知有两个线程,一个是执行main()方法的主线程main,另个是main线程通过Thread.start() 方法启动的业务线程ReturnalbleTask.main线程通过Thread.start()启动ReturnalbleTask线程之后,会继续做自己的事,ReturnalbleTask 线程开始并发执行, ReturnalbleTask 线程首先执行的是thread.run()方法,然后在其中会执行到target(futureTask任务) 的run()方法,接着在futureTask.run()方法中执行futureTask的callable成员的call()方法.FutureTask的callable成员的call()方法执行完成后,会将结果保存在FutureTask内部的outCome实例属性中.

main线程和returnableTask线程的执行流程大致如下:
在这里插入图片描述

通过线程池创建线程

前面三种方式创建的Thread实例在执行完成后就销毁了,这些线程实例都是不可复用的. 在高并发的应用常见下,是要求线程能够复用的,若想线程可以复用,就需要使用线程池技术.java中提供了了一个静态工厂来创建不同的线程池 --Executors工厂类.

使用Executors创建线程池,然后使用ExecutorService线程池执行或者提交target执行目标实例的示例代码如下:

package com.th.Executors;

import com.th.util.ThreadUtil;

import java.util.concurrent.*;

/**
 * @ClassName: CreateDemo
 * @Description:
 * @Author: 唐欢
 * @Date: 2022/8/12 14:18
 * @Version 1.0
 */
public class CreateDemo {

    public  static final int MAX_TURE=5;
    public  static final  int COMPUTE_TIMES=100000000;
    //创建一个包含三个线程的线程池
    private  static ExecutorService pool = Executors.newFixedThreadPool(3);

    static  class DemoThread implements  Runnable{

        @Override
        public void run() {
            for (int j=1;j{

        // 返回并发执行的时间
        @Override
        public Long call() throws Exception {
            Long startTime = System.currentTimeMillis();
            System.out.println(ThreadUtil.getCurThreadName() +"线程运行开始,");
            Thread.sleep(1000);
            for (int i=0;i

补充ThreadUtil类的代码:

public class ThreadUtil {

    public  static  String getCurThreadName(){
        //获取线程名称
        return  Thread.currentThread().getName();
    }
}
  • 相关阅读:
    2023品牌新媒体矩阵营销洞察报告:流量内卷下,如何寻找增长新引擎?
    这里有你想知道的ES2020中Javascript10个新特性
    我的JQuery随笔
    【yolov5】pytorch模型导出为onnx模型
    深度学习数据集的文本制作和读取
    SpringMVC之文件上传下载
    矩阵分析与应用+张贤达
    @Transactional注解在类上还是接口上使用,哪种方式更好?
    基于YOLOv8模型的水果目标检测系统(PyTorch+Pyside6+YOLOv8模型)
    Charles抓包
  • 原文地址:https://blog.csdn.net/tang_huan_11/article/details/126304183