今天聊聊多线程

简介: 今天聊聊多线程

多线程在Java开发领域算是比较常见, 工作中的业务代码都会使用多线程来提高执行效率, 充分的利用cpu多核的特性, 把电脑的性能发挥到极致。如何创建多线程呢?其实很简单, Java 提供了4种方式。

1、继承 Thread 类,重写run方法

2、实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread 构造函数的target

3、通过Callable和futureTask 创建线程

4、通过线程池创建线程


前面两种可以归结为一类,没有返回值, 原因很简单,通过重写run方法, run方法的返回值都是void, 所以没有返回结果。


方式1:继承Thread 类的线程实现方式如下

public class ThreadR extends Thread {
    public ThreadR() {
        //编写子类的构造方法,可缺省
        System.out.println("编写子类的构造方法");
    }
    @Override
    public void run() {
        //编写自己的线程代码
        System.out.println(Thread.currentThread().getName());
    }
    public static void main(String[] args) {
        ThreadR threadDemo01 = new ThreadR();
        threadDemo01.setName("我是自定义的线程1");
        threadDemo01.start();
        System.out.println(Thread.currentThread().toString());
    }
}

执行结果:

编写子类的构造方法

Thread[main,5,main]

我是自定义的线程1


方式2:通过实现Runnable 接口,实现run方法,接口的实现类的实例对象作为Thread的target作为参数传入带参的Thread构造函数,通过调用start()方法启动线程。

实现Runnable接口的线程实现方式如下

public class ThreadRun implements Runnable {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        Thread thread = new Thread(new ThreadRun());
        thread.start();
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 通过接口的方式实现的 ");
    }
}

运行的结果为:

main

Thread-0 通过接口的方式实现的


线程实现方式3:通过Callable和futureTask 创建线程

a:创建Callable 接口的实现类, 并实现Call 方法

b:创建Callable实现类的实现, 使用futureTask 类包装Callable 对象, 该FutureTask 对象封装了Callable对象的Call 方法的返回值

c:使用FutureTask对象作为Thread对象的target创建并启动线程

d:调用FutureTask对象的get()来获取子线程执行结束的返回值

public class CallFutureV {
    public static void main(String[] args) {
        Callable<Object> callable = new CallFutureVK<Object>();
        FutureTask<Object> futureTask = new FutureTask<Object>(callable);
        Thread thread = new Thread(futureTask);
        System.out.println(" 通过Callable 的方式创建 " + Thread.currentThread().getName());
        thread.start();
    }
    static class CallFutureVK<Object> implements Callable<Object>{
        /**
         * 重写call 方法
         * @return
         * @throws Exception
         */
        @Override
        public Object call() throws Exception {
            System.out.println(Thread.currentThread().getName()+"------ 通过实现Callable接口 通过 futureTask 包装器实现的线程");
            return null;
        }
    }
}

程序运行结果:

通过Callable 的方式创建main

Thread-0------ 通过实现Callable接口 通过 futureTask 包装器实现的线程


方式4:通过线程池来创建


         

执行结果如下:

线程池参数定义的说明

ThreadPoolExecutor 参数介绍

ThreadPoolExecutor 最多可以设置7个参数, 如下代码显示

public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler) {
     // 省略...
 }

7个参数代表的含义如下:

参数1:corePoolSize

核心线程数, 线程池中始终存活的线程数。

参数2:maximumPoolSize

最大线程数,线程池中允许的最大线程数, 当线程池的任务队列满了之后可以创建的最大线程数。

参数3:keepAliveTime

最大线程数可以存活的时间,当线程中没有任务执行时, 最大线程就会销毁一部分, 最终保持核心线程数量的线程。

参数4:unit:

单位是和参数3存活时间配合使用的,合在一起用于设定线程的存活时间, 参数keepAliveTime 的时间单位有以下7种可选:

TimeUnit.DAYS:天
TimeUnit.HOURS:小时
TimeUnit.MINUTES:分
TimeUnit.SECONDS:秒
TimeUnit.MILLISECONDS:毫秒
TimeUnit.MICROSECONDS:微妙
TimeUnit.NANOSECONDS:纳秒

参数5:workQueue

一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全, 包含7种类型

ArraryBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保存它们。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能
从中提取元素。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似
还含有非阻塞方法。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

较常用的是 LinkedBlockingQueue 和 Synchronous , 线程池的排队策略与 BlockingQueue 有关


参数6:threadFactory

线程工厂, 用来创建线程,默认为正常优先级, 非守护线程。

参数7:handler

拒绝策略, 拒绝处理任务时的策略, 系统提供了4种可选

AbortPolicy:拒绝并抛出异常。
CallerRunsPolicy:使用当前调用的线程来执行此任务。
DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务, 并执行当前任务。
DiscardPolicy:忽略并抛弃当前任务。

默认策略为 AbortPolicy


总结:

创建线程的4种方式也都介绍了, 但最主要的还是通过线程池来创建线程的方式。  这种方式的好处是能解决频繁创建线程带来的损耗。 创建线程池去管理线程,创建回收这种方式最大的解决了资源浪费的情况。

下节重点介绍线程池的方式使用和线程池创建后的执行流程。


目录
相关文章
|
Windows
Windows系统 services.msc命令详解,Windows命令行查看本地服务
第一步、打开cmd 按下 win 键,输入 cmd 后回车,打开「命令提示符」
1556 0
Windows系统 services.msc命令详解,Windows命令行查看本地服务
|
Kubernetes 负载均衡 网络协议
详解 Kubernetes 的稳定性和可用性
大家好,我叫杨朝乐,来自才云科技基础设施部门。今天给大家分享一个平时可能接触得较少的话题:关于 Kubernetes 的稳定性和可用性。 下面是今天分享以下 5 个主题: 认识稳定性 认识异常 Kubernetes 里面的高可用方案 如何处理异常 我的经验分享 认识稳定性 Kubernetes 集群的稳定性和众多因素相关。
3128 1
|
监控
阿里云应用性能管理(APM)产品-应用实时监控服务(ARMS)技术解密 资料下载
直播大纲 1. 应用性能管理(APM)背景介绍 2. 分布式链路追踪的现状与使用场景 3. ARMS分布式链路追踪的技术实现 4. 最佳实践 (1) 全息排查+场景链路(2) 前端监控与应用监控融合(3) ARMS与K8S的融合与实践 专家介绍 阳其凯(逸陵),阿里巴巴高级开发工程师,2016年加入阿里巴巴Eageleeye团队,多年实时计算平台与APM产品开发经验,目前主要负责云产品业务实时监控服务(ARMS)与链路追踪(Tracing Analysis)的研发工作。
13548 0
|
12月前
|
SQL 存储 人工智能
化整为零:湖仓数据平台一站式迁移
本文介绍了湖仓平台迁移的概况、痛点及解决方案。首先概述了数据湖和数据仓库迁移的现状与背景,强调其重要性及挑战。接着分析了迁移过程中的主要痛点,如数据量大、业务变更频繁等。最后提出了一种化整为零的新范式,通过精细化设计和自动化工具提升迁移效率,并展示了一站式湖仓迁移中心的关键阶段和产品大图,旨在加速迁移过程并减少人工成本。
|
11月前
|
Perl
|
IDE 编译器 开发工具
C/C++开发环境
C/C++开发环境
324 4
|
算法 数据库连接 数据库
魔鬼数字的起源与在编程中的警示作用
魔鬼数字的起源与在编程中的警示作用
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
556 0
|
存储 程序员 定位技术
程序员必知:安卓的四大组件
程序员必知:安卓的四大组件
420 0
|
传感器 存储 数据采集
技术好文共享:焕新!CANape19真香!
技术好文共享:焕新!CANape19真香!