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
方法:
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
来创建的线程
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…...

我的零分周赛:CSDN周赛第30期,成绩“0”分,天然气定单、小艺读书、买苹果、圆桌
CSDN周赛第30期,成绩“0”分,天然气定单、小艺读书、买苹果🍎、圆桌。 (本文获得CSDN质量评分【91】)【学习的细节是欢悦的历程】Python 官网:https://www.python.org/ Free:大咖免费“圣经”教程《 python 完全自学教…...

二、Java虚拟机的基本结构
Java虚拟机的架构1.内存结果概述2.类加载器子系统的作用3. 类加载器ClassLoader角色4.类的加载过程5.类加载器的分类1.引导类加载器(虚拟机自带的加载器)Bootstrap ClassLoader2.扩展类加载器(虚拟机自带的加载器) Extenssion ClassLoader3.应用程序类加载器(虚拟机自带的加载器…...

华为OD机试 - 用户调度(Python) | 机试题+算法思路+考点+代码解析 【2023】
用户调度 题目 在通信系统中有一个常见的问题是对用户进行不同策略的调度,会得到不同系统消耗的性能。 假设由N个待串行用户,每个用户可以使用A/B/C三种不同的调度策略。 不同的策略会消耗不同的系统资源,请你根据如下规则进行用户调度,并返回总的消耗资源数。 规则是: …...

HashMap(JDK1.8)源码+底层数据结构分析
HashMap 简介底层数据结构分析 JDK1.8 之前JDK1.8 之后 HashMap 源码分析 构造方法put 方法get 方法resize 方法 HashMap 常用方法测试 感谢 changfubai 对本文的改进做出的贡献! HashMap 简介 HashMap 主要用来存放键值对,它基于哈希表的 Map 接口实现…...

case的使用
1.x和z值 1.1.定义 x:表示不定值 z:表示高阻态,还有一种表达方式“?” 一个x/z可以用来定义十六进制(h)数的4位二进制的状态,八进制(o)数的3位,二进制&#x…...

Mac配置ITerm2
Mac配置ITerm2 install-shell-integration配置lrzsz配置zsh安装Oh-My-Zsh修改皮肤文件加载皮肤添加插件配置profiles 1.expect配置文件2.shell脚本 iterm2顶部白条闪烁 install-shell-integration 安装完成之后会有一个指示标,需要弄掉Preferences > Profiles …...

JUC并发编程(下)
✨作者:猫十二懿 ❤️🔥账号:CSDN 、掘金 、个人博客 、Github 🎉公众号:猫十二懿 学习地址 写在最前 JUC并发编程(上) JUC(Java Util Concurrent)学习内容框架&…...

API接口的基础知识
API是应用程序编程接口(Application Programming Interface)的缩写,能够起到两个软件组件之间的连接器或中介的作用。此类接口往往通过一组明确的协议,来表示各种原始的请求和响应。API文档可以向开发人员展示请求和响应是如何形成…...

基于Spring Boot的教务管理系统
文章目录项目介绍主要功能截图:登录首页学生信息管理班级信息管理教师信息管理教师评价部分代码展示设计总结项目获取方式🍅 作者主页:Java韩立 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题…...

网页扫描图像并以pdf格式上传到服务器端
本文描述如何通过网页驱动扫描仪、高拍仪等图像扫描设备进行图像扫描,扫描结果经编辑修改后以pdf压缩格式上传到后台java程序中进行服务器端落盘保存。图像扫描上传如文字描述顺序所介绍,先要驱动扫描设备工作,进行纸张数据的光学扫描操作形成…...