Java实现多线程有几种方式(满分回答)
目录
- JDK8 创建的线程的两种方式
- orcle文档解释
- 方式一:继承Thread类
- 方式二:实现Runnable接口
- 同时用两种的情况
- 其他间接创建方式
- Callable接口
- 线程池
JDK8 创建的线程的两种方式
orcle文档解释
orcle文档:https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
总结:
准确的讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种方式:
- 方法一:实现
Runnable接口的重写run方法,并把Runnable实例传给Thread类 - 方法二∶重写
Thread的run方法(继承Thread类)
(其他一些方式,究其根本都是间接的通过上面两种方式创建的)
方式一:继承Thread类
public static void main(String[] args) {System.out.println("主线程运行:" + Thread.currentThread().getName());Task1 task = new Task1();task.start();}static class Task1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 20; i++) {System.out.println("task1线程运行" + Thread.currentThread().getName());System.out.println("Task11继承Thread实现多线程");}}}}
自定义线程类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
方式二:实现Runnable接口
public static void main(String[] args) {Task task = new Task();Thread thread = new Thread(task);thread.start();}static class Task implements Runnable{@Overridepublic void run() {for (int i = 0; i < 20; i++) {System.out.println("task1线程运行" + Thread.currentThread().getName());System.out.println("Task11实现Runnable接口实现多线程");}}}
定义MyRunnable类实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
同时用两种的情况
public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println("实现Runnable的run方法");}}){@Overridepublic void run() {System.out.println("匿名内部类,重写Thread类的run方法");}}.run();
}
结果:
匿名内部类,重写Thread类的run方法
分析:
这里使用的匿名内部类,在匿名内部类里面又重写了Thread类的run方法。创建Thread类的同时传入了Runnable对象,这时候Thread类的run已被重写,不再是原生方法的逻辑:执行传入task的run
所以即便传入Runnable,也不会被执行。
其他间接创建方式
Callable接口
public static void main(String[] args) {Task task = new Task();FutureTask<Integer> ft = new FutureTask<>(task);Thread thread = new Thread(ft);thread.start();}static class Task implements Callable<Integer> {@Overridepublic Integer call() throws Exception {for (int i = 0; i < 20; i++) {System.out.println("task1线程运行" + Thread.currentThread().getName());System.out.println("Task11实现Callable接口实现多线程");}return 1;}
}
FutureTask实现了RunnableFuture接口,RunnableFuture继承了Runnable 和 Future接口。所以也是间接通过Runnable来创建的。
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future result1 = ser.submit(t1);
- 获取结果: boolean r1 = result1.get()
- 关闭服务: ser.shutdownNow();
实现Runnable接口和Callable接口的区别
- Runnable 接口不会返回结果,Callable 接口可以返回结果
- Callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出
线程池
ThreadPoolExecutor类的execute方法:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AEKGtqyC-1677038180765)(Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B1%87%E6%80%BB%E6%80%BB%E7%BB%93%E7%AC%94%E8%AE%B0.assets/image-20230222115102680.png)]](https://img-blog.csdnimg.cn/62a7d83443f54065a634affd7761cd73.png)
public void execute(Runnable command) {if (command == null)throw new NullPointerException();/** Proceed in 3 steps:** 1. If fewer than corePoolSize threads are running, try to* start a new thread with the given command as its first* task. The call to addWorker atomically checks runState and* workerCount, and so prevents false alarms that would add* threads when it shouldn't, by returning false.** 2. If a task can be successfully queued, then we still need* to double-check whether we should have added a thread* (because existing ones died since last checking) or that* the pool shut down since entry into this method. So we* recheck state and if necessary roll back the enqueuing if* stopped, or start a new thread if there are none.** 3. If we cannot queue task, then we try to add a new* thread. If it fails, we know we are shut down or saturated* and so reject the task.*/int c = ctl.get();//如果工作线程数小于核心线程数,if (workerCountOf(c) < corePoolSize) {//执行addWorker,会创建一个核心线程,如果创建失败,重新获取ctlif (addWorker(command, true))return;c = ctl.get();}//如果工作线程数大于等于核心线程数,线程池的状态是RUNNING,并且可以添加进队列//(RUNNING状态下)如果添加失败,说明是队列已经满了,接着就去创建新的线程,如果大于最大线程数,则执行拒绝策略//如果线程池不是RUNNING状态,则执行拒绝策略(当然还会调addWorker进行判断一次)if (isRunning(c) && workQueue.offer(command)) {//再次获取ctl,进行双重检索(也就是对线程池的状态再次检查一遍)int recheck = ctl.get();//如果线程池是不是处于RUNNING的状态,那么就会将任务从队列中移除,//如果移除失败,则会判断工作线程是否为0 ,如果过为0 就创建一个非核心线程//如果移除成功,就执行拒绝策略,因为线程池已经不可用了;if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))//线程池挂了或者大于最大线程数reject(command);
}
addWork方法会创建线程池内工作线程Worker。Worker这个线程,实现了Runnable接口,并持有一个线程thread,一个初始化的任务firstTask。thread是调用构造方法时通过ThreadFactory来创建的线程
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TG3ITFgx-1677038180766)(Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B1%87%E6%80%BB%E6%80%BB%E7%BB%93%E7%AC%94%E8%AE%B0.assets/image-20230222113722589.png)]](https://img-blog.csdnimg.cn/ad7c5f04b34241df8f3be7b47e0ab973.png)
ThreadFactory中也是Runnable创建线程的模式。

相关文章:
Java实现多线程有几种方式(满分回答)
目录JDK8 创建的线程的两种方式orcle文档解释方式一:继承Thread类方式二:实现Runnable接口同时用两种的情况其他间接创建方式Callable接口线程池JDK8 创建的线程的两种方式 orcle文档解释 orcle文档:https://docs.oracle.com/javase/8/docs…...
实例4:树莓派GPIO控制舵机转动
实例4:树莓派GPIO控制舵机转动 实验目的 通过背景知识学习,了解舵机的外观及基本运动方式。了解四足机器人mini pupper腿部单个舵机的组成结构。通过GPIO对舵机进行转动控制,熟悉PWM。了解mini pupper舵机组的整体调零。 实验要求 使用Py…...
【音视频处理】为什么MP3不是无损音乐?音频参数详解,码率、采样率、音频帧、位深度、声道、编码格式的关系
大家好,欢迎来到停止重构的频道。上期我们讨论了视频的相关概念,本期我们讨论音频的相关概念。包括采样率、码率、单双声道、音频帧、编码格式等概念。这里先抛出一个关于无损音频的问题。为什么48KHz采样率的.mp3不是无损音乐 ,而48KHz采样率…...
Linux 环境变量
Linux 环境变量能帮你提升 Linux shell 体验。很多程序和脚本都通过环境变量来获取系统信息、存储临时数据和配置信息。在 Linux 系统上有很多地方可以设置环境变量,了解去哪里设置相应的环境变量很重要。 认识环境变量 bash shell 用环境变量(environme…...
从功能测试(点点点)到进阶自动化测试,实现薪资翻倍我只用了3个月时间
前言 从事测试工作已3年有余了,今天想聊一下自己刚入门时和现在的今昔对比,虽然现在也没什么成就,只能说笑谈一下自己的测试生涯,各位看官就当是茶余饭后的吐槽吧,另外也想写一写自己的职场感想,希望对刚开…...
aspnetcore 原生 DI 实现基于 key 的服务获取
你可能想通过一个字符串或者其他的类型来获取一个具体的服务实现,那么在 aspnetcore 原生的 MSDI 中,如何实现呢?本文将介绍如何通过自定义工厂来实现。我们现在恰好有基于 Json 和 MessagePack 的两种序列化器有一个接口是这样的publicinter…...
华为OD机试 -最大子矩阵和(Python) | 机试题+算法思路+考点+代码解析 【2023】
最大子矩阵和 题目 给定一个二维整数矩阵 要在这个矩阵中 选出一个子矩阵 使得这个子矩阵内所有的数字和尽量大 我们把这个子矩阵成为“和最大子矩阵” 子矩阵的选取原则,是原矩阵中一段相互连续的矩形区域 输入 输入的第一行包含两个整数N,M (1 <= N,M <= 10) 表示…...
C2驾照科一学习资料(1)
目录 记1分 记3分 记6分 记9分 记12分 你有不伤别人的教养 却缺少一种不被人伤的气场 若没人护你周全 就请善良中带点锋芒为自己保驾护航 这个世界你若好到毫无保留 对方就会坏到肆无忌惮 记1分 《道路交通安全违法行为记分管理办法》规定,机动车驾驶人有下列…...
4576: 移动数组元素
描述给定一个n个元素的一维数组,将下标从0到p的元素全部平移到数组尾部。输入第一行有两个正整数n和p(2<n<100,0<p<n)。第二行有n个整数,表示数组的各个元素。输出在一行中按顺序输出移动后的各个数组元素…...
字符串中<br>处理
需求: 后端返回的字符串中带有br换行符,前端需要处理行内及行尾的换行符。具体需求可分为以下两个: 若是字符串末尾有换行符,需要去掉。若是字符串内有换行符,有两种需求:①将换行符转换成逗号或其它符号&…...
大数据技术原理与应用介绍
大数据技术原理与应用 概述 大数据不仅仅是数据的“大量化”,而是包含“快速化”、“多样化”和“价值化”等多重属性。 两大核心技术:分布式存储和分布式处理 大数据计算模式 批处理计算流计算图计算查询分析计算 大数据具有数据量大、数据类型繁…...
【Python】序列与列表(列表元素的增删改查,求之,列表推导式、列表的拷贝)
一、序列序列的概念:按照某种顺序排列的数据类型就叫做序列,比如字符串,列表,元组,集合序列的共同点是都有下标,支持index()方法和count(),也支持切片处理(等同于字符串序列的切片处理)l1 [0, …...
update导致死锁
update delete 操作,如果走的索引,对索引和主键索引加行锁 如果没有走索引,锁整张表。 不开启事务,mysql本身也会加锁 一般MYSQL在执行CREATE,ALTER,INSERT等命令时会自动加锁 在对数据进行更新操作时 如果update没用到索引&…...
Java 集合 --- 如何遍历Map
Java 集合 --- 如何遍历MapMap的基本操作如何遍历MapType of HashMapMap没有继承Collection接口AbstractMap和AbstractCollection是平级关系 Map的基本操作 package map; import java.util.*; /*** This program demonstrates the use of a map with key type String and val…...
C#从值类型、引用类型到装箱和拆箱
上一篇文章讲了C#的值类型和引用类型,这里再来看看值类型和引用类型最直接的使用场景:装箱和拆箱。 一、基本概念 装箱:值类型转化为引用类型的过程。从托管堆中为新生成的引用类型对象分配内存,再把值类型的实例字段拷贝到托管堆上新对象的…...
Java中的逻辑运算符/移位运算符简单总结
前段时间刷到了力扣关于位运算的题,这里浅浅记录一下! 1. 逻辑位运算 1.1 与 & &:按位与进行二进制计算,规则是同为1则为1,不同为0,具体如下: 0&00, 0&10, 1&00, 1&…...
活动预告 | GAIDC 全球人工智能开发者先锋大会
大会主题——“向光而行的 AI 开发者” 2023 全球人工智能开发者先锋大会(GAIDC) 由世界人工智能大会组委会、上海市经济和信息化委员会、上海市人才工作领导小组办公室及中国(上海)自由贸易试验区临港新片区管理委员会指导&…...
【Linux系统】认识操作系统和操作系统如何进行管理以及进程相关状态
进程概念1 认识冯诺依曼体系结构1.1 冯诺依曼体系结构存储器的作用2 操作系统(Operator System,OS)2.1 OS如何进行管理3 进程3.1 OS管理进程:先描述再组织3.2 描述进程-PCB3.3 查看进程3.4 通过系统调用获取进程标识符3.5 通过系统调用创建子进程——for…...
【0基础学爬虫】爬虫基础之HTTP协议的基本原理介绍
大数据时代,各行各业对数据采集的需求日益增多,网络爬虫的运用也更为广泛,越来越多的人开始学习网络爬虫这项技术,K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章,为实现从易到难全方位覆盖,特设【0基础学…...
SpringBoot 整合定时任务
注解概览 EnableScheduling 在配置类上使用,开启计划任务的支持(类上) Scheduled 来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持) pom依赖 <parent…...
如何快速上手Redux Dynamic Modules:5分钟完成Redux模块化改造
如何快速上手Redux Dynamic Modules:5分钟完成Redux模块化改造 【免费下载链接】redux-dynamic-modules Modularize Redux by dynamically loading reducers and middlewares. 项目地址: https://gitcode.com/gh_mirrors/re/redux-dynamic-modules Redux Dyn…...
CentOS 8/Stream 8系统DNF换源后,安装软件还是慢?试试这几个排查命令和优化技巧
CentOS 8/Stream 8系统DNF换源后安装缓慢的深度排查与优化指南当你已经按照教程将CentOS 8/Stream 8的DNF源切换为国内镜像,却发现软件安装速度依然不尽如人意时,这种体验确实令人沮丧。作为长期使用CentOS系统的技术专家,我完全理解这种&quo…...
如何优化 MySQL 千万级数据分页查询的性能?
它的本质是:**传统 LIMIT offset, size 在大数据量下性能急剧下降,是因为 MySQL 必须 扫描并丢弃 前 offset 行数据。当 offset 很大时(如 LIMIT 1000000, 10),MySQL 需要读取 1,000,010 行记录,执行 1,000…...
8款网盘直链下载助手:彻底告别限速烦恼,实现高速下载自由
8款网盘直链下载助手:彻底告别限速烦恼,实现高速下载自由 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移…...
终极虚拟显示器解决方案:ParsecVDisplay完整使用指南
终极虚拟显示器解决方案:ParsecVDisplay完整使用指南 【免费下载链接】parsec-vdd ✨ Perfect virtual display for game streaming 项目地址: https://gitcode.com/gh_mirrors/pa/parsec-vdd ParsecVDisplay是一个基于Parsec虚拟显示驱动(VDD)的独立应用程序…...
【独家首发】Sora 2 AVI支持并非“开箱即用”:3层封装校验机制详解(RIFF→AVI→OpenCV Mat内存映射链路图解)
更多请点击: https://codechina.net 第一章:Sora 2 AVI支持并非“开箱即用”:核心矛盾与技术定位 Sora 2 的官方文档与发布说明中明确将 AVI 视为“实验性容器支持”,而非默认启用的输入格式。其底层解码栈基于 FFmpeg 5.1 构建&…...
具身智能的发展对人类社会的影响有哪些?
具身智能对人类社会影响一、经济产业层面产业重构:催生机器人、智能制造、自动驾驶新产业,重塑生产链条效率跃升:替代重复繁重劳作,工厂、农业、物流产能大幅提升就业结构变化:低端体力岗位缩减,运维、研发…...
终极解决方案:彻底解决UE4SS DLL劫持导致的系统级应用程序启动错误
终极解决方案:彻底解决UE4SS DLL劫持导致的系统级应用程序启动错误 【免费下载链接】RE-UE4SS Injectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games 项目地址: https://gitcode.com/gh_mirrors/r…...
暗黑破坏神2存档编辑器:d2s-editor免费可视化编辑终极指南
暗黑破坏神2存档编辑器:d2s-editor免费可视化编辑终极指南 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 想要轻松修改暗黑破坏神2存档却不懂十六进制?d2s-editor是你的完美解决方案!这款基于…...
基于傅里叶神经算子的含时密度泛函理论加速模拟
1. 项目概述与核心思路在计算材料科学和量子化学领域,模拟电子在激光等外场驱动下的动力学行为,是理解光催化、光电转换、甚至量子计算基础过程的关键。含时密度泛函理论(TDDFT)是处理这类问题的“金标准”之一,它通过…...
