【多线程】Java中多线程的几种实现方式

        

        多线程(multithreading)是指在一个程序中同时执行多个不同的线程(thread),每个线程都是程序的一部分,是独立的执行路径。相比于单线程程序,多线程程序可以更充分地利用计算机的多核心或多处理器资源,提高程序的运行效率和用户体验。在多线程编程中需要注意线程同步、锁、死锁等问题。

        常见的多线程实现方式有以下几种:

1、继承Thread类方式

        这种方式是定义一个类继承Thread类,然后重写run()方法,将要执行的任务写在run()方法中。创建线程实例后,调用start()方法启动线程。该方式实现简单,但如果类已经继承了其他类,则无法使用。

以下是一个继承Thread类的多线程案例:

class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " " + i);}}
}public class Main {public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();thread1.start();thread2.start();}
}

        这个案例中,我们创建了一个继承了Thread类的MyThread类,并在它的run方法中打印了当前线程的名字和一个循环计数器。然后在主函数中,我们创建了两个MyThread对象并启动它们。当程序运行时,两个线程会交替打印出它们的计数器值,因为它们是同时运行的。

        这个案例展示了如何继承Thread类来创建一个线程,并且启动线程的方法是调用start方法。每个线程都有自己的执行上下文,包含自己的栈空间和局部变量,因此两个线程之间的计数器值是相互独立的。

2、实现Runnable接口方式

        Java实现Runnable接口方式的代码如下:

  1. 定义一个类实现Runnable接口,重写run()方法
public class MyRunnable implements Runnable {@Overridepublic void run() {// 编写线程执行的任务代码for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

  1. 创建MyRunnable实例并将其作为参数传入Thread类的构造方法中
public class Main {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();}
}

  1. 启动线程并执行任务

        当调用Thread的start()方法时,JVM会自动调用MyRunnable实例的run()方法,从而执行线程的任务。

        注意:在这种方式下,多个线程可以共享同一个MyRunnable实例,这意味着多个线程可以同时执行同一个任务。如果需要多个线程执行不同的任务,需要为每个线程创建一个单独的Runnable实例,并将其传入Thread的构造方法中。

        这种方式是定义一个类实现Runnable接口,重写run()方法,将要执行的任务写在run()方法中。调用Thread类的构造函数,将该实现Runnable接口的对象作为参数传入,创建Thread实例后调用start()方法启动线程。该方式相比于继承Thread类方式,更加灵活,因为一个类可以实现多个接口。

3、实现Callable接口方式

        Callable接口是Java提供的一个线程执行结果的返回值接口,需要通过Future接口获取执行结果。下面是Java实现Callable接口的示例代码:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i <= 10; i++) {sum += i;}return sum;}public static void main(String[] args) throws Exception {MyCallable callable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();Integer result = futureTask.get();System.out.println("计算结果:" + result);}
}

        在上面的代码中,我们通过实现Callable接口,实现了call()方法,返回了一个Integer类型的结果。我们使用FutureTask类将Callable对象封装成一个异步任务,然后启动一个线程执行这个异步任务。最后,通过FutureTask的get()方法获取异步任务的执行结果。

        注意:FutureTask的get()方法会阻塞当前线程,直到异步任务执行完毕并返回结果。如果异步任务执行时间过长,会导致当前线程一直阻塞。为了避免这种情况,我们可以使用FutureTask的get(long timeout, TimeUnit unit)方法,设置一个超时时间,超过这个时间后如果异步任务还没有执行完毕,则抛出TimeoutException异常,当前线程继续执行。

        该方式类似于实现Runnable接口方式,但是需要实现Callable接口的call()方法,并返回一个结果。可以通过FutureTask类将Callable转换成Runnable来启动线程并获取返回值。

4、实现线程池方式

        Java中线程池可以通过以下步骤实现:

        1. 创建一个ExecutorService对象,它是Java中线程池的基本实现类之一,也是一个抽象类,可以使用Executors类中的静态方法来创建。

ExecutorService executorService = Executors.newFixedThreadPool(5);

        这里创建了一个固定大小为5的线程池。

        2. 在需要执行任务的地方,使用submit方法提交一个Runnable对象或Callable对象。

executorService.submit(new Runnable() {@Overridepublic void run() {// 执行任务代码}
});

        3. 执行完任务后,记得关闭线程池。

executorService.shutdown();

线程池的好处在于它可以避免重复创建和销毁线程,从而提高了程序的性能和效率。同时,线程池还可以控制并发线程的数量,防止系统崩溃。

        通过使用线程池,可以更好地管理和复用线程,避免了线程的频繁创建和销毁。使用线程池是一种高效的多线程实现方式。

        每种方式都有各自的特点,需要根据具体的实际情况选择合适的方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/277200.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch: 基于【VGG16】处理MNIST数据集的图像分类任务【准确率98.9%+】

目录 引言在Conda虚拟环境下安装pytorch步骤一&#xff1a;利用代码自动下载mnist数据集步骤二&#xff1a;搭建基于VGG16的图像分类模型步骤三&#xff1a;训练模型步骤四&#xff1a;测试模型运行结果后续模型的优化和改进建议完整代码结束语 引言 在本博客中&#xff0c;小…

对不起,必须放弃SQL!

尽管SQL很受欢迎&#xff0c;也很成功&#xff0c;但它又总是充斥着种种矛盾。 SQL可能笨拙又冗长&#xff0c;但开发人员又经常发现它往往是他们提取所需数据的最简单直接的方法。当查询写入正确时&#xff0c;它可以快如闪电&#xff0c;当查询出错时&#xff0c;它就会慢如…

elementUi表单验证 上一下两项都有必填校验,添加v-if后失效

需在el-form-item 在不一样的key区分就在页面会生效

ConnectionError怎么解决

文章目录 解决思路lz的具体解决过程 解决思路 这个错误表明在尝试加载评价指标时&#xff0c;代码试图从 huggingface 下载文件&#xff0c;但由于代理错误而无法连接。 为了解决这个问题&#xff0c;你可以尝试以下几个步骤&#xff1a; 1.设置代理&#xff1a; 如果你在使…

jmeter接口测试项目实战详解,零基础也能学

1.什么是jmeter&#xff1f; JMeter是100%完全由Java语言编写的&#xff0c;免费的开源软件&#xff0c;是非常优秀的性能测试和接口测试工具&#xff0c;支持主流协议的测试 2.jmeter能做什么&#xff1f; 1.JMeter是100%完全由Java语言编写的软件性能测试的GUI的测试工具&…

X86汇编语言:从实模式到保护模式(代码+注释)--c10、11(保护模式:32位x86处理器编程架构+进入保护模式)

保护模式&#xff1a;32位x86处理器编程架构 IA-32架构的基本执行环境 寄存器扩展 通用寄存器&#xff08;32&#xff09;&#xff1a;EAX EBX ECX EDX ESI EDI EBP ESP 指令寄存器&#xff08;32&#xff09;&#xff1a;EIP 标志寄存器&#xff08;32&#xff09;&#xff…

路由器的转换原理--ENSP实验

目录 一、路由器的工作原理 二、路由表的形成 1、直连路由 2、非直连路由 2.1静态路由 2.2动态路由 三、静态路由和默认路由 1、静态路由 1.1静态路由的缺点 1.2路由的配置--结合ensp实验 2、默认路由--特殊的静态路由 2.1概念 2.2格式 2.3默认路由的配置--ens…

如何在 JavaScript 中实现任务队列

任务队列的概念 任务队列就是存放任务的队列&#xff0c;队列中的任务都严格按照进入队列的先后顺序执行。 在前一条任务执行完毕后&#xff0c;立即执行下一条任务&#xff0c;直到任务队列清空。 任务队列的基本执行流程如下&#xff1a; 设置任务队列并发数&#xff1b; …

VMP泄露编译的一些注意事项

VMP编译教程 鉴于VMP已经在GitHub上被大佬强制开源&#xff0c;特此出一期编译教程。各位熟悉的可以略过&#xff0c;不熟悉的可以参考一下。 环境&#xff08;软件&#xff09; Visual Studio 2015 - 2022 &#xff08;建议使用VS2019&#xff0c;Qt插件只有这个版本及以上…

【改进YOLOv8】生猪胖瘦评价分级系统:可重参化EfficientRepBiPAN优化Neck

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义&#xff1a; 随着计算机视觉和深度学习的快速发展&#xff0c;目标检测成为了计算机视觉领域的一个重要研究方向。目标检测的目标是在图像或视频中准确地识别和定…

如何购买最新卫星影像?

虽然该网站提供的卫星影像更新频率快&#xff0c;且覆盖率高&#xff0c;但分辨率却只有10米。 如果工作中需要用到最新的卫星影像&#xff0c;且分辨率要求高&#xff0c;也许付费购买才是解决问题的最佳途径。 卫星影像主要分光学卫星影像和SAR影像两种&#xff0c;由于我们…

C++ stringOJ练习题

目录 把字符串转换成整数 反转字符串 字符串中的第一个唯一字符 字符串最后一个单词的长度 找出字符串中第一个只出现一次的字符 字符串相加 字符串最后一个单词长度 字符串相乘 反转字符串3 反转字符串2 验证回文串 把字符串转换成整数 通过遍历字符串并逐位转换…