Java多线程--JDK5.0新增线程创建方式

文章目录

  • 一、新增方式1:实现Callable接口
    • (1)介绍
    • (2)案例
    • (3)总结对比
  • 二、新增方式2:使用线程池
    • (1)问题与解决思路
      • 1、现有问题
      • 2、解决思路
      • 3、好处
    • (2)线程池相关API
    • (3)案例

一、新增方式1:实现Callable接口

(1)介绍

【Callable接口】

@FunctionalInterface
public interface Callable<V> {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}

<>是泛型的意思,V决定了call()方法返回值的类型。

  • 与使用Runnable相比, Callable功能更强大些
    • 相比run()方法,可以有返回值。
    • 方法可以抛出异常。
    • 支持泛型的返回值(需要借助FutureTask类,获取返回结果)。
  • Future接口(了解)
    • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
    • FutureTask是Futrue接口的唯一的实现类。
    • FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
  • 缺点:在获取分线程执行结果的时候,当前线程(或是主线程)受阻塞,效率较低。

(2)案例

【步骤】

1、创建一个实现Callable的实现类。

2、实现call方法,将此线程需要执行的操作声明在call()中。

3、创建Callable接口实现类的对象。

4、将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象。

5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()

6、获取Callablecall方法的返回值。

【案例】

🌱代码

package yuyi04.callable;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;/*** ClassName: Callable* Package: yuyi04.callable* Description:*  创建多线程的方式三:实现Callable (jdk5.0新增的)* @Author 雨翼轻尘* @Create 2024/2/3 0003 13:59*///1.创建一个实现Callable的实现类
class NumThread implements Callable {//2.实现call方法,将此线程需要执行的操作声明在call()中@Overridepublic Object call() throws Exception {int sum = 0;for (int i = 1; i <= 100; i++) {	//遍历100以内的偶数if (i % 2 == 0) {System.out.println(i);sum += i;	//计算100以内偶数的和}Thread.sleep(1000);}return sum;}
}public class CallableTest {public static void main(String[] args) {//3.创建Callable接口实现类的对象NumThread numThread = new NumThread();//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象FutureTask futureTask = new FutureTask(numThread);//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()Thread t1 = new Thread(futureTask);t1.start();System.out.println("main()线程");try {//6.获取Callable中call方法的返回值//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。Object sum = futureTask.get();System.out.println("总和为:" + sum);} catch (Exception e) {e.printStackTrace();}}}

🍺输出(部分)

image.png

☕注意

call方法就相当于之前的run方法,与run方法不一样的是,call方法可以有返回值类型

另外,call方法可以通过throws抛出异常。也就是异常可以不在里面处理了,可以往外抛,更加灵活了。

之前在run方法里面,sleep方法有一个异常,可能会出现编译时异常InterruptedException,所以就会在同步代码块里面做try-catch操作。如下:

class PrintNumber implements Runnable{//...@Overridepublic void run() {while(true){synchronized (obj) {   //obj是唯一的,线程安全//...if(number<=100){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//...}else{break;}}}}
}

如果就想在外面知道方法调用可能出现了什么异常,也可以在这里throw一个运行时异常,将编译时异常InterruptedException以字符串的方式传进去,如下:

image.png

这就是一种处理方式,在外面想要知道出现了什么异常,也能抛,只不过此时抛的是一个运行时异常,运行时异常通常不处理。

就避开了在sleep的地方直接throws出去。

相当于将可能出现的编译时异常以一个运行时异常的方式给抛出

而现在的Callable比较灵活,可以直接将异常抛出去。

而且它抛的还是Exception,也就是说,无论是编译时异常还是运行时异常都可以。如下:

image.png

call方法可以返回值,而run方法不行,比如上边案例中,run方法就只能将sum定义在外面,在里面给sum赋值,方法执行结束以后,sum的值也就变了,不能以返回的方式去体现了。


FutureTask也实现了Runnable的接口,所以第5步使用的Thread构造器还是这个:

public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}

这个start()仍然执行的是实现类NumThread里面的call方法。

对于当前操作来说,有一个返回值Object。如果想获取这个返回值的话,就需要使用到futureTsak

image.png

拿着futureTsak去调用get方法,就能得到总和sum,如下:

image.png

🎲说个容易混淆的事情。

Object sum = futureTask.get();这个操作是在主线程当中做的。

之前说过这样一个例子:

public class ThreadTest {public static void main(String[] args) {// 创建线程对象MyThread t = new MyThread();t.setName("线程1");t.start();// 调用sleep方法try {t.sleep(1000 * 5);} catch (InterruptedException e) {e.printStackTrace();}// 5秒之后这里才会执行。System.out.println("hello World!");}
}class MyThread extends Thread {public void run() {for (int i = 0; i < 10000; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i);}}
}

sleep在哪个线程里面执行,就让哪个线程sleep了。

若是将sleep写在run方法里面了,就是分线程sleep;在主线程里面,就是让主线程sleep了。

还有一种解释,sleep()静态方法,静态方法可以用对象来调用,若对象是null的话也照样可以调。

换句话说,如果将t写成了MyThread,会更加明确。

如下:

image.png

说白了,就是sleep()在哪个线程里面调用的,就让哪个线程sleep


🚗小细节

这里t1.start(),分线程start(),就出去执行了。

image.png

那么主线程会马上执行futureTask.get()吗?

若分线程还没有遍历完100以内的偶数,这时候要是主线程执行的话就会拿不到sum最终的值?

其实不会的。这里会有一个阻塞。这个阻塞是自然而然的。

主线程会一直等分线程return只有分线程return了,主线程才会get

image.png

(3)总结对比

创建多线程的方式三:实现Callable (jdk5.0新增的)

Runnable方式的对比的好处

  • call()可以有返回值,更灵活。
  • call()可以使用throws的方式处理异常,更灵活。
  • Callable使用了泛型参数,可以指明具体的call()的返回值类型(不一定非要是Object),更灵活。

缺点吗?

如果在主线程中需要获取分线程call()返回值,则此时的主线程是阻塞状态的。

image.png

这种方式存在的意义,估计就是笔试题了。

在开发当中,最常使用的就是“线程池”。

二、新增方式2:使用线程池

(1)问题与解决思路

1、现有问题

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,即执行完一个任务,并不被销毁,而是可以继续执行其他的任务

2、解决思路

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中

可以避免频繁创建销毁、实现重复利用。

类似生活中的公共交通工具。

image.png

将创建好的线程都放在一个池子里面,就叫线程池

若此时一系列的任务过来了,每一个任务需要拿一个线程去操作,那么这个任务就可以去线程池里面捞一个线程来用。

假设现在只有三个线程,对应三个任务拿着它们去执行。如果还有其他新的任务,则需要等待。

当现有的任务执行完之后,此时的线程不会因为当前任务的执行结束而销毁,而是又还回到线程池当中,这个线程就可以继续分配给其他任务去执行。

这就得到了很好的重复利用。

当然,我们还可以设置线程池的参数,比如初始化的时候有三个线程池,不够的时候再增加线程。

3、好处

创建多线程的方式四:使用线程池

此方式的好处:

  • 提高了程序执行的效率,提高响应速度。(因为线程已经提前创建好了,减少了创建新线程的时间)
  • 提高了资源的复用率。(因为执行完的线程并未销毁,而是可以继续执行其他的任务)
  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 。
  • 可以设置相关的参数,对线程池中的线程的使用进行管理。(便于线程管理)
    • corePoolSize:核心池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

(2)线程池相关API

  • JDK5.0之前,我们必须手动自定义线程池。从JDK5.0开始,Java内置线程池相关的API。在java.util.concurrent包下提供了线程池相关API:ExecutorServiceExecutors
  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
    • void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
    • <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable
    • void shutdown() :关闭连接池
  • Executors:一个线程池的工厂类,通过此类的静态工厂方法可以创建多种类型的线程池对象。
    • Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
    • Executors.newFixedThreadPool(int nThreads); 创建一个可重用固定线程数的线程池
    • Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
    • Executors.newScheduledThreadPool(int corePoolSize):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

(3)案例

🌱代码

package com.atguigu06.createmore.pool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;//创建并使用多线程的第四种方法:使用线程池class NumberThread implements Runnable{@Overridepublic void run() {	//指明线程想干什么for(int i = 0;i <= 100;i++){	//打印偶数if(i % 2 == 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}class NumberThread1 implements Runnable{@Overridepublic void run() {for(int i = 0;i <= 100;i++){	//打印奇数if(i % 2 != 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}public class ThreadPool {public static void main(String[] args) {//1. 提供指定线程数量的线程池ExecutorService service = Executors.newFixedThreadPool(10);ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;	//强转
//        //设置线程池的属性
//        System.out.println(service.getClass());//ThreadPoolExecutorservice1.setMaximumPoolSize(50); //设置线程池中线程数的上限//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象service.execute(new NumberThread());//适合适用于Runnableservice.execute(new NumberThread1());//适合适用于Runnable//        service.submit(Callable callable);//适合使用于Callable//3.关闭连接池service.shutdown();}}

🍺输出(部分)

image.png

☕注意

造线程是通过“new Thread”来体现的,这里只是指明了要做的事情,还没有线程的概念。

如下:

image.png

然后通过Executors.newFixedThreadPool(10)造了一个线程池(细节就不用管了),池子里面初始化放了10个线程。

可以设置相关参数:

image.png

现在只需要告诉我你想干什么,就new一个Runnable的实现类的对象,其实就是帮忙调用一下run方法。

image.png

这里没有看到new Thread的代码,因为第1步造线程池的时候已经做好了。

所以就可以将要做的事情的run方法封装在对象里面,往这里的小括号一放,就可以自动执行上面的run方法了。

如下:

image.png

最后,若是这个池子不用了,就可以将它删了。

image.png

线程池也可以适配Callable,Callable如果想要有返回值的话,可以使用submit方法。

在开发当中,如果线程多次使用,其实选择的都是“线程池”的方式。

而这种池子通常要么是直接封装好了,要是用的话,可以像上面案例一样去使用。

具体线程池的使用,在JUC里面说,Java基础阶段不做深入。

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

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

相关文章

Swift Vapor 教程(查询数据、插入数据)

上一篇简单写了 怎么创建 Swift Vapor 项目以及在开发过程中使用到的软件。 这一篇写一个怎么在创建的项目中创建一个简单的查询数据和插入数据。 注&#xff1a;数据库配置比较重要 先将本地的Docker启动起来&#xff0c;用Docker管理数据库 将项目自己创建的Todo相关的都删掉…

以小猪o2o生活通v17.1为例简要分析SWOOLE加密破解,swoole_loader加密破解swoole加密逆向后的代码修复流程(个人见解高手掠过)

现在用Php加密五花八门除了组件就是混淆&#xff0c;在组件里面响当当的还属swoole&#xff0c;SWOOLEC是不错的国产加密&#xff0c;值得推荐官方宣称是永远无法破解的加密算法&#xff0c;针对swoole compiler的代码修复我谈谈我的看法&#xff0c;以小猪o2o生活通&#xff0…

PyTorch 2.2 中文官方教程(十九)

使用 RPC 进行分布式管道并行 原文&#xff1a;pytorch.org/tutorials/intermediate/dist_pipeline_parallel_tutorial.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 作者&#xff1a;Shen Li 注意 在github中查看并编辑本教程。 先决条件&#xff1a; PyTorc…

04-Java建造者模式 ( Builder Pattern )

建造者模式 摘要实现范例 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象 一个Builder 类会一步一步构造最终的对象&#xff0c;该 Builder 类是独立于其他对象的 建造者模式属于创建型模式&#xff0c;它提供了一种创建对…

深度学习手写字符识别:训练模型

说明 本篇博客主要是跟着B站中国计量大学杨老师的视频实战深度学习手写字符识别。 第一个深度学习实例手写字符识别 深度学习环境配置 可以参考下篇博客&#xff0c;网上也有很多教程&#xff0c;很容易搭建好深度学习的环境。 Windows11搭建GPU版本PyTorch环境详细过程 数…

vcruntime140.dll最新的修复方法,一键修复vcruntime140.dll的手段

在这篇文章中&#xff0c;我们将深入探讨并详细介绍各种修复vcruntime140.dll文件缺失或损坏问题的方法。鉴于此类问题广泛存在并影响了众多用户&#xff0c;本文目的是向大家展示不同的修复策略&#xff0c;希望能够帮助每个人解决这些棘手的技术难题。下面一起来看看vcruntim…

【RT-DETR有效改进】UNetv2提出的一种SDI多层次特征融合模块(细节高效涨点)

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、本文介绍 本问给大家带来的改进机制是UNetv2提出的一种多层次特征融合模块(SDI)其是一种用于替换Concat操作的模块,SDI模块的主要思想是通过整合编码器生成的层级特征图来增强图像中的语义信息和细节信息。包括皮肤…

黑豹程序员-ElementPlus选择图标器

ElementPlus组件提供了很多图标svg 如何在你的系统中&#xff0c;用户可以使用呢&#xff1f; 这就是图标器&#xff0c;去调用ElementPlus的icon组件库&#xff0c;展示到页面&#xff0c;用户选择&#xff0c;返回选择的组件名称。 效果 代码 <template><el-inpu…

机器学习 - 梯度下降

场景 上一章学习了代价函数&#xff0c;在机器学习中&#xff0c;代价模型是用于衡量模型预测值与真实值之间的差异的函数。它是优化算法的核心&#xff0c;目标是通过调整模型的参数来最小化代价模型的值&#xff0c;从而使模型的预测结果更接近真实值。常见的代价模型是均方…

【Boost】:searcher的建立(四)

searcher的建立 一.初始化二.搜索功能三.完整源代码 sercher主要分为两部分&#xff1a;初始化和查找。 一.初始化 初始化分为两步&#xff1a;1.创建Index对象&#xff1b;2.建立索引 二.搜索功能 搜索分为四个步骤 分词&#xff1b;触发&#xff1a;根据分词找到对应的文档…

架构设计特训

一、考点分布 软件架构风格&#xff08;※※※※&#xff09;层次型软件架构风格&#xff08;※※※※&#xff09;面向服务的软件架构风格&#xff08;※※※※&#xff09;云原生架构风格&#xff08;※※※※&#xff09;质量属性与架构评估&#xff08;※※※※※&#xff…

Transformer实战-系列教程1:Transformer算法解读1

&#x1f6a9;&#x1f6a9;&#x1f6a9;Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 Transformer实战-系列教程1&#xff1a;Transformer算法解读1 Transformer实战-系列教程2&#xff1a;Transformer算法解读2 现在最火的AI内容&#xff0c;chatGPT、视觉大模…

Golang切片与数组

在Go语言中&#xff0c;切片&#xff08;Slice&#xff09;和数组&#xff08;Array&#xff09;是两个核心的数据结构&#xff0c;它们在内存管理、灵活性以及性能方面有着显著的区别。接下来将解析Golang中的切片与数组&#xff0c;通过清晰的概念解释、案例代码和实际应用场…

小林Coding_操作系统_读书笔记

一、硬件结构 1. CPU是如何执行的 冯诺依曼模型&#xff1a;中央处理器&#xff08;CPU&#xff09;、内存、输入设备、输出设备、总线 CPU中&#xff1a;寄存器&#xff08;程序计数器、通用暂存器、指令暂存器&#xff09;&#xff0c;控制单元&#xff08;控制CPU工作&am…

[word] word页面视图放大后,影响打印吗? #笔记#学习方法

word页面视图放大后&#xff0c;影响打印吗&#xff1f; word文档的页面视图又叫普通视图&#xff0c;又叫打印视图&#xff0c;是系统默认的视图&#xff0c;是用户用的最多最常见的视图。 问&#xff1a;怎样打开页面视图&#xff1f; 答&#xff1a;两种方法 方法一、点…

JS 基本语句

函数调用&#xff0c;分支&#xff0c;循环&#xff0c;语句示例。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"&g…

【Funny guys】龙年专属测试鼠标寿命小游戏...... 用Python给大家半年了......

目录 【Funny guys】龙年专属测试鼠标寿命小游戏...... 用Python给大家半年了...... 龙年专属测试鼠标寿命小游戏用Python给大家半年了贪吃龙游戏 文章所属专区 码农新闻 欢迎各位编程大佬&#xff0c;技术达人&#xff0c;以及对编程充满热情的朋友们&#xff0c;来到我们的程…

【项目实践03】【布隆过滤器】

文章目录 一、前言二、项目背景三、实现方案1. 谷歌 布隆过滤器2. Redis 布隆过滤器 四、思路延伸1. 布隆过滤器的实现原理2. 布隆过滤器的一些扩展3. 布谷鸟过滤器 五、参考内容 一、前言 本系列用来记录一些在实际项目中的小东西&#xff0c;并记录在过程中想到一些小东西&a…

基于python+控制台的车辆信息管理系统

基于python控制台的车辆信息管理系统 一、系统介绍二、效果展示三、其他系统实现四、获取源码 一、系统介绍 打印功能菜单、添加车辆信息、删除车辆信息、修改车辆信息、显示车辆信息、退出系统&#xff0c;并且需要接收用户的输入&#xff0c;在根据输入内容调用相应函数实现…

Docker部署Grafana+Promethus监控Mysql和服务器

一、Grafana部署所需资源 Grafana 需要最少的系统资源&#xff1a; 建议的最小内存&#xff1a;512 MB建议的最低 CPU&#xff1a;1 官方文档&#xff1a;https://grafana.com/docs/grafana/latest/getting-started/build-first-dashboard/ 可以看到&#xff0c;我的这台服务…