iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >多线程案例(4)-线程池
  • 391
分享到

多线程案例(4)-线程池

java开发语言java-ee多线程 2023-08-19 13:08:05 391人浏览 泡泡鱼
摘要

文章目录 多线程案例四四、线程池 大家好,我是晓星航。今天为大家带来的是 多线程案例-线程池 相关的讲解!😀 多线程案例四 四、线程池 线程池是什么 虽然创建线程 / 销毁线程 的开销 想象这么一个场景:

大家好,我是晓星航。今天为大家带来的是 多线程案例-线程池 相关的讲解!😀

多线程案例四

四、线程池

线程池是什么

虽然创建线程 / 销毁线程 的开销

想象这么一个场景:

在学校附近新开了一家快递店,老板很精明,想到一个与众不同的办法来经营。店里没有雇人, 而是每次有业务来了,就现场找一名同学过来把快递送了,然后解雇同学。这个类比我们平时来一个任务,起一个线程进行处理的模式。

很快老板发现问题来了,每次招聘 + 解雇同学的成本还是非常高的。老板还是很善于变通的,知道了为什么大家都要雇人了,所以指定了一个指标,公司业务人员会扩张到 3 个人,但还是随着 业务逐步雇人。于是再有业务来了,老板就看,如果现在公司还没 3 个人,就雇一个人去送快 递,否则只是把业务放到一个本本上,等着 3 个快递人员空闲的时候去处理。这个就是我们要带出的线程池的模式。

🧨线程池最大的好处就是减少每次启动、销毁线程的损耗。🧨

标准库中的线程池

  • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
  • 返回值类型为 ExecutorService
  • 通过 ExecutorService.submit 可以注册一个任务到线程池中.
ExecutorService pool = Executors.newFixedThreadPool(10);pool.submit(new Runnable() {    @Override    public void run() {        System.out.println("hello");   }});

上述代码第一行代码就是创建了一个线程池,池子里线程数目固定是10个。

这个操作,使用某个类的某个静态方法,直接构造出了一个对象来(相当于是把 new 操作,给隐藏到这样的方法后面了)

像这样的方法,就称为"工厂方法"

提供工厂方法的类,也就成为"工厂类"

此处这个代码就使用了"工厂模式"这种 设计模式

那么什么是工厂模式呢?

答:工厂模式,一句话表示,使用普通的方法来代替构造方法,创建对象。为啥要代替呢,因为构造方法有坑,他只能构造一种对象,如果要构造多种不同情况的对象,就难搞了。

例如我们需要使用笛卡尔坐标系来构造坐标,空间直角坐标系和极坐标系,那么此时我们的代码就会是:

class Point {    //构造空间直角坐标系    public Point(double x, double y) {            }    //构造极坐标系    public Point(double r, double a) {            }}

显然此时我们虽然可以用代码写出并构造出这两个坐标系,但是他们是❌错误的,因为我们的构造方法只能构造一个对象。而普通方法是可以构造多个对象的

那么就会有人问了我们不是可以通过重载的方法来进行多个构造方法吗,此时我们来看一下重载的要求,重载要求的是:方法名相同,参数的个数或者类型不相同。

因此我们这里根本不可能进行重载调用。

普通方法构建笛卡尔坐标系代码:

class PointFactory {    public static Point makePointByXY(double x,double y) {            }    public static Point makePointByRA(double r,double a) {            }}public class ThreadDemo26 {    public static void main(String[] args) {        Point P = PointFactory.makePointByXY(10,20);    }}

普通方法,方法名字没有限制。因此有多种方式构造,就可以直接使用不同的方法名即可。此时,方法的参数是否要区分,已经不重要了

这里我们区分一下重载和重写的区别:

重载(overload):指一个类中可以有多个方法具有相同的名字,但这些方法的参数不同(参数的类型和个数不同)。分别在父类子类里也是可能构成重载的。

重写(override):在不同类中(指父类和子类)中,两个具有相同方法名和相同参数的方法,称作重写。 (如果是其他语言,重写方法不一定通过父类子类)

重写本质上就是用一个新的方法,代替旧的… 就得要求新的方法和旧的方法,名字/参数都得一摸一样

下面我们为大家带来几个线程池的使用举例:

运行程序之后发现,main线程结束了,但是整个进程没有结束。线程池中的线程都是 前台线程。此时会阻止进程结束。(前面定时器 Timer 也是同理)

下属代码为什么不能直接写 System.*out*.println("hello" + n); 呢?

答:因为变量捕获,这里的 i 是主线程里的局部变量(在主线程的栈上),随着主线程这里的代码块执行结束就销毁了。很可能主线程这里的 for 执行完了,当前 run 的任务在线程池里还没排到呢,此时 i 就销毁了

变量捕获(必须是不可修改的变量才能捕获 有final修饰的变量也可捕获):在定义 run 的时候,偷偷把 i 当前的值记住。后续执行run的时候,就创建一个也叫做 i 的局部变量,并且把这个值赋值过去

final:

final修饰类,表示该类是无法被任何其他类继承的,意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。

final修饰类中的方法,表示该类是无法被任何其他类继承的,不可以被重写;也就是把该方法锁定了,以防止继承类对其进行更改。

final修饰类中的变量,表示该变量一旦被初始化便不可改变。

此处要注意,当前是往线程池里放了1000个任务,1000个任务就是让这 10 个线程来平均分配一下,差不多一个线程执行100个任务,但是注意这里并非是严格的平均,可能有的多一个有的少一个,都很正常。(每个线程都执行完一个任务之后,再立即取下一个任务,由于每个任务执行时间都差不多,因此每个线程做的任务数量就差不多)

进一步的可以认为,这1000个任务,就在一个队列里排队,这10个线程,就依次来取队列中的任务,取一个就执行一个,执行完了之后再执行下一个。

因为线程调度是随机分配的,因此会出现顺序不一样的情况。

Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数(可能是多个)的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer. 执行的时候不是由扫描线程自己执行,而是由单独的线程池来执行。

总结:实践中确定的线程数量,也很简单,通过测试/实验的方式

Executors 本质上是 ThreadPoolExecutor 类的封装.

ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定. (后面再介绍)

实现线程池

  • 核心操作为 submit, 将任务加入线程池中 使用 Worker 类描述一个工作线程.
  • 使用 Runnable 描述一个任务.
  • 使用一个 BlockingQueue 组织所有的任务
  • 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
  • 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增 线程了.
class Worker extends Thread {    private LinkedBlockingQueue<Runnable> queue = null;    public Worker(LinkedBlockingQueue<Runnable> queue) {        super("worker");        this.queue = queue;   }    @Override    public void run() {        // try 必须放在 while 外头, 或者 while 里头应该影响不大        try {            while (!Thread.interrupted()) {                Runnable runnable = queue.take();                runnable.run();           }       } catch (InterruptedException e) {       }   }}
public class MyThreadPool {    private int maxWorkerCount = 10;    private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue();    public void submit(Runnable command) {        if (workerList.size() < maxWorkerCount) {            // 当前 worker 数不足, 就继续创建 worker            Worker worker = new Worker(queue);            worker.start();       }        // 将任务添加到任务队列中        queue.put(command);   }    public static void main(String[] args) throws InterruptedException {        MyThreadPool myThreadPool = new MyThreadPool();        myThreadPool.execute(new Runnable() {            @Override            public void run() {                System.out.println("吃饭");           }       });  Thread.sleep(1000);   }}

自己实现线程池版本二:

import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;class MyThreadPoll {    //此处不涉及 "时间",此处只有任务,就直接使用 Runnable 即可~~    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();    //n 表示线程的数量    public MyThreadPoll(int n) {        //在这里创建线程        for (int i = 0; i < n; i++) {            Thread t = new Thread(()->{               while (true) {                   try {                       Runnable runnable = queue.take();                       runnable.run();                   } catch (InterruptedException e) {                       e.printStackTrace();                   }               }            });            t.start();        }    }    //注册任务给线程池    public void submit(Runnable runnable) {        try {            queue.put(runnable);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}public class ThreadDemo27 {    public static void main(String[] args) {        MyThreadPoll pool = new MyThreadPoll(10);        for (int i = 0; i < 1000; i++) {            int n = i;            pool.submit(new Runnable() {                @Override                public void run() {                    System.out.println("hello" + n);                }            });        }    }}

四个拒绝策略:

例如我们此时在学习,而有好朋友叫我们去打游戏

那么对应上面情况:

  1. 要学习的东西太多,我直接开摆,啥也不干了…
  2. 学习任务太多了,让朋友们自己去玩。(此时朋友们自己去打游戏了)
  3. 直接不学了,开玩!
  4. 拒绝去玩,继续学习。(至于朋友们有没有玩我们不必管)

感谢各位读者的阅读,本文章有任何错误都可以在评论区发表你们的意见,我会对文章进行改正的。如果本文章对你有帮助请动一动你们敏捷的小手点一点赞,你的每一次鼓励都是作者创作的动力哦!😘

来源地址:https://blog.csdn.net/xinhang10/article/details/132129808

--结束END--

本文标题: 多线程案例(4)-线程池

本文链接: https://www.lsjlt.com/news/375476.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 多线程案例(4)-线程池
    文章目录 多线程案例四四、线程池 大家好,我是晓星航。今天为大家带来的是 多线程案例-线程池 相关的讲解!😀 多线程案例四 四、线程池 线程池是什么 虽然创建线程 / 销毁线程 的开销 想象这么一个场景: ...
    99+
    2023-08-19
    java 开发语言 java-ee 多线程
  • Java多线程案例之线程池
    文章目录 一. 线程池概述1. 什么是线程池2. Java标准库提供的线程池 二. 线程池的简单实现 一. 线程池概述 1. 什么是线程池 线程池和和字符串常量池, 数据库连接池一样,...
    99+
    2023-09-04
    java 线程池 多线程
  • C#多线程之线程池(ThreadPool)
    一、简介 前面介绍了平时用到的大多数的多线程的例子,但在实际开发中使用的线程往往是大量的和更为复杂的,这时,每次都创建线程、启动线程。从性能上来讲,这样做并不理想(因为每使用一个线程...
    99+
    2022-11-13
  • 多线程编程(3):线程池ThreadPo
    在面向对象编程中,经常会面对创建对象和销毁对象的情况,如果不正确处理的话,在短时间内创建大量对象然后执行简单处理之后又要销毁这些刚刚建立的对象,这是一个非常消耗性能的低效行为,所以很多面向对象语言中在内部使用对象池来处理这种情况,以提高性能...
    99+
    2023-01-31
    线程 多线程 ThreadPo
  • Java多线程常见案例分析线程池与单例模式及阻塞队列
    目录一、单例模式1、饿汉模式2、懒汉模式(单线程)3、懒汉模式(多线程)二、阻塞队列阻塞队列的实现生产者消费者模型三、线程池1、创建线程池的的方法(1)ThreadPoolExecu...
    99+
    2022-11-13
  • 线程池之newFixedThreadPool定长线程池的实例
    newFixedThreadPool定长线程池的实例 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newFixedThr...
    99+
    2022-11-12
  • C#多线程系列之线程池
    目录线程池ThreadPool 常用属性和方法线程池说明和示例线程池线程数线程池线程数说明不支持的线程池异步委托任务取消功能计时器线程池 线程池全称为托管线程池,线程池受 .NET ...
    99+
    2022-11-13
  • Python 多线程爬取案例
    目录前言一、多进程库(multiprocessing)二、多线程爬虫三、案例实操四、案例解析1、获取网页内容2、获取每一章链接3、获取每一章的正文并返回章节名和正文4、将每一章保存到...
    99+
    2022-11-11
  • 线程池之newCachedThreadPool可缓存线程池的实例
    java线程池: Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,...
    99+
    2022-11-12
  • Python中线程池模块之多线程的示例分析
    这篇文章将为大家详细讲解有关Python中线程池模块之多线程的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1、线程池模块引入from concurrent.futures i...
    99+
    2023-06-15
  • Python异步爬虫多线程与线程池示例详解
    目录背景异步爬虫方式多线程,多进程(不建议)线程池,进程池(适当使用)单线程+异步协程(推荐)多线程线程池背景 当对多个url发送请求时,只有请求完第一个url才会接着请求第二个ur...
    99+
    2022-11-12
  • C#多线程之线程池ThreadPool详解
    一、ThreadPool概述 提供一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。 创建线程需要时间。如果有不同的小任务要完成,就可...
    99+
    2022-11-13
  • C#多线程之线程池ThreadPool用法
    目录一、ThreadPool1、QueueUserWorkItem()2、GetMaxThreads()3、GetMinThreads()4、SetMaxThreads()和SetM...
    99+
    2022-11-13
  • Java多线程之线程池七个参数的示例分析
    这篇文章主要介绍Java多线程之线程池七个参数的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!ThreadPoolExecutor是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它提供了任务提交...
    99+
    2023-06-14
  • Java多线程与线程池技术分享
    目录一、序言1、普通执行2、线程池执行二、线程池基础1、核心参数2、参数与池的关系1、通用对比2、拓展对比3、无返回值任务4、有返回值任务三、Executors1、创建单一线程的线程...
    99+
    2022-11-13
  • Java多线程 自定义线程池详情
    主要介绍: 1.任务队列 2.拒绝策略(抛出异常、直接丢弃、阻塞、临时队列) 3.init( min ) 4.active 5.max ...
    99+
    2022-11-12
  • Java多线程并发与并行和线程与进程案例
    目录一、并发与并行二、线程与进程三、创建线程类前言: 程序在没有跳转语句的前提下,都是由上至下依次执行,那现在想要设计一个程序,边打游戏边听歌,怎么设计? 要解决上述问题,咱们得使用...
    99+
    2022-11-13
  • Android的线程、多线程和线程池面试题有哪些
    这篇“Android的线程、多线程和线程池面试题有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android的线程、多...
    99+
    2023-06-04
  • java怎么使用线程池启动多线程
    在 Java 中,可以使用线程池来启动多线程。以下是使用线程池启动多线程的示例代码:首先,需要导入 `java.util.concu...
    99+
    2023-09-15
    java
  • 【JavaEE】多线程案例-阻塞队列
    1. 前言 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是: 在队列为空时,获取元素的线程会等待队列变为非空当队列满时,存储元素的线程会等待队列可用 阻塞队列...
    99+
    2023-09-16
    java-ee java 开发语言 阻塞队列
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作