首页 > 其他分享 >线程池是啥有啥用,怎么用,如何自己实现一个

线程池是啥有啥用,怎么用,如何自己实现一个

时间:2024-09-16 23:49:55浏览次数:3  
标签:女神 Thread 池是 如何 任务 线程 备胎 public

目录

一、线程池是啥,有啥用

二、线程池怎么用 

1.构造方法 

2.如何使用Java的线程池  

三、简单实现一个线程池


假设我是一个(好看+有才华) 的妹子,那么我就会有很多追求者,这些也叫备胎们,我们若把他们放到一个地方,那就叫“备胎池”。同理,线程就叫“线程池”。

一、线程池是啥,有啥用

频繁的创建销毁线程开销太大,所以我们会先创建一些线程放着,要用的时候直接拿就行了,这些线程都放一块地方,用完之后再放回这个地方,那么我们管这个地方叫“线程池”。

 所以:线程池最⼤的好处就是减少每次启动、销毁线程的损耗。

 

那么为什么线程池的效率更高,开销更小呢? 

 那么就得谈谈:用户态和内核态

比如说:

1)在银行你要办业务,可能会需要身份证或者户口本复印件,这时候,你如果没带,那么银行可能会有复印机。

2)那么你有2种选择:第一种是去柜台让柜员帮你,第二种是自己去大厅的复印机复印。

3)内核态:如果让柜员帮你去后台复印,可能没这么快,柜员也许要帮别人也复印,也可能去摸鱼,也可能去上个厕所。我们知道操作系统是由内核和软件组成,有很多软件都需要内核管理,可能会没那么及时。

4)用户态:如果你是自己去大厅复印,自给自足,非常的快。

5)而我们的线程池就是用户态,自己创建线程是内核态,所以效率会比线程池低。

二、线程池怎么用 

 我们有专门的类表示线程池:ThreadPoolExecutor

1.构造方法 

下面是它的构造方法,最主要的是第4个最为全面,其他三个都是从第4个演化出来的。

 我们分析一下这个线程池的参数:

  • corePoolSize:正式员⼯的数量. (正式员⼯, ⼀旦录⽤, 永不辞退)
  • maximumPoolSize: 正式员⼯ + 临时⼯的数⽬. (临时⼯: ⼀段时间不⼲活, 就被辞退)
  • keepAliveTime: 临时⼯允许的空闲时间
  • unit: keepaliveTime 的时间单位, 是秒, 分钟, 还是其他值
  • workQueue: 传递任务的阻塞队列
  • threadFactory: 创建线程的⼯⼚, 参与具体的创建线程⼯作,通过不同线程⼯⼚创建出的线程相当于 对⼀些属性进⾏了不同的初始化设置
  • RejectedExecutionHandler: 拒绝策略, 如果任务量超出公司的负荷了接下来怎么处理 
    1. AbortPolicy(): 超过负荷, 直接抛出异常          
    2. CallerRunsPolicy(): 调⽤者负责处理多出来的任务     
    3. DiscardOldestPolicy(): 丢弃队列中最⽼的任务
    4. DiscardPolicy(): 丢弃新来的任务  

1)corePoolSize和maximumPoolSize中,如果核心线程数是M个,在使用的过程中发现不够用,会自动扩容多M个,直至最大线程数max个。

 2)keepAliveTime和unit是姐妹,一起用的,就是实习生(非核心线程)太久没活干了,就会裁掉它,怎么样才算太久呢?就需要你自己设置一个时间

3)workQueue是我们需要传进去的堵塞队列,线程池就是一个生产者消费者模型,程序员把任务submit放进线程池中。这个队列可以自己设计容量和类型。

4)线程工厂,是为了弥补构造方法的不足,比如假设我在二维中需要表示一个点,我有2种方法,第一种是横纵坐标,第二种是用极坐标,

但是这样的话重载就失效了,因为名字参数什么的啥都一样啊,构成不了重载。

而是需要工厂设计模式解决,如上图。

5)拒绝策略(重点):当线程池的要执行的任务满了,它就会进行堵塞。但是有时候这未必是件好事。比如说你女神直接拒绝你,比起干耗着你好,这样你就能开启新的生活了。

假设线程池是女神,你是任务,但是备胎太多了忙不过来。

第一个就是女神拒绝了你,你崩溃了,其他备胎也崩溃了(没想到女神有这么多备胎);

第二个女神不想你追她,让你去追其他小姐姐,但是女神依旧和备胎们约会;

第三个是把追的最久的备胎扔掉,没新鲜感了,和最新追我的约会;

第四个是把最新的备胎踢掉,是个念旧的女神,就当一切没有发生,继续和之前的备胎们约会。

2.如何使用Java的线程池  

虽然ThreadPoolExecutor固然强大,但是参数太多,用起来确实麻烦,所以Java标准库中有更方便的:Executor提供了一些工厂方法,比如:

接受线程池的类型:ExecutorService

创建便捷线程池的方法:newFixedThreadPool(),把核心线程数和最大线程数设置成一样,不会自动扩容

添加任务到线程池执行:submit() 

public static void main(String[] args) throws InterruptedException {
        ExecutorService service=Executors.newFixedThreadPool(4);
        for(int i=0;i<100;i++){
            int id=i;
        service.submit(()->{
            Thread current=Thread.currentThread();
            System.out.println("hello world "+id+ " "+current.getName());
        });

        }
        Thread.sleep(2000);
        service.shutdown();
        System.out.println("程序结束");
    }

三、简单实现一个线程池

需要如下条件:

一个堵塞队列queue,来装要执行的任务,通过take取任务。

线程池类,来用for循环放线程(相当于线程池),这个类有submit()方法用queue队列中用put就能放进去执行这些线程中。

(由于拒绝策略太麻烦了,这里没有写出来,感兴趣的可以自行写一下)

class MyThreadPool {
    private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);

    //创造线程
    public MyThreadPool(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) throws InterruptedException {
        queue.put(runnable);
    }
}

public class demo15 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool myThreadPool=new MyThreadPool(4);
        for(int i=0;i<1000;i++){
            int id=i;
            myThreadPool.submit(()->{
                System.out.println("执行任务 "+id+" "+Thread.currentThread().getName());
            });
        }
    }

}
  •  核⼼操作为 submit,将任务加⼊线程池中
  •  使⽤ Worker 类描述⼀个⼯作线程。使⽤ Runnable 描述⼀个任务.
  •  使⽤⼀个 BlockingQueue 组织所有的任务
  •  每个 worker 线程要做的事情:不停的从 BlockingQueue 中取任务并执⾏
  •  指定⼀下线程池中的最⼤线程数 maxWorkerCount;当当前线程数超过这个最⼤值时,就不再新增线程了

标签:女神,Thread,池是,如何,任务,线程,备胎,public
From: https://blog.csdn.net/2301_80958683/article/details/142306973

相关文章

  • 如何修改边框的外观
    文章目录1.概念介绍2.使用方法3.示例代码我们在上一章回中介绍了DrawerHeaderWidget相关的内容,本章回中将介绍BoxDecorationWidget.闲话休提,让我们一起TalkFlutter吧。1.概念介绍我们在这里介绍的BoxDecorationWidget是一种修饰类组件,它不能单独使用,需......
  • 微信小程序的多级选择器(multiSelector模式的Picker)如何设计出各列之间的可选项有关联
    在微信小程序中,我们常常会需要使用到选择器(Picker),而选择器有一种模式叫做multiSelector,即多级选择。这个选择器可用于提交多个维度的信息,例如在餐厅里点餐时,需要同时提交要点的蔬菜、荤菜、主食、汤的信息。关于选择器,使用的方法见picker|微信开放文档。这里简单说明一下多......
  • 【Java】深入理解Java中的多线程同步机制
    一、多线程的数据不一致    当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。    这个时候,一个在单线程模型下不存在的问题就会发生:如果多个线程同时读写共享......
  • 支持线程编排的并行框架AsyncTool
    它是由京东零售开源的项目,作者是天涯泪小武。如果大家想更深入理解可以去作者的博客看一下。为什么会学习这个框架最近在学习java并发中的CompletableFuture,它除了提供了更为好用和强大的Future特性之外,还提供了函数式编程、异步任务编排组合(可以将多个异步任务串联起来......
  • 如何利用Python进行数据分析与可视化的具体操作指南
    成长路上不孤单......
  • 【自动化测试】常见的自动化遍历工具以及如何选择合适的自动化遍历工具
    引言自动化遍历测试通常依赖于特定的工具来实现应用的自动操作和测试文章目录引言一、常见的自动化遍历工具1.1Appium1.2Selenium1.3Calabash1.4RobotFramework1.5Espresso1.6XCTest1.7Macaca1.8TestComplete1.9UiAutomator1.10总结二、如何选择合适的自......
  • 关于数据在内存中如何存储
    1.整数在内存中的存储在讲解操作符的时候,我们就讲过了下⾯的内容:整数的2进制表⽰⽅法有三种,即原码、反码和补码有符号的整数,三种表⽰⽅法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”,⽤1表⽰“负”,最⾼位的⼀位是被当做符号位,剩余的都是数值位。 正整数的原、反、......
  • 如何学习JAIN-SLEE
    要系统地学习JAIN-SLEE(JavaAPIforIntegratedNetworks–ServiceLogicExecutionEnvironment),你需要从基础概念到高级应用逐步深入学习。以下是详细的入门学习路径和顺序,涵盖必要的知识点、学习顺序和步骤,帮助你快速掌握JAIN-SLEE。1.掌握基础知识JAIN-SLEE是......
  • JAIN-SLEE 架构及如何运作
    JAIN-SLEE(JavaAPIforIntegratedNetworks-ServiceLogicExecutionEnvironment)是一种架构,用于构建实时电信应用程序,如电话呼叫控制、短信发送、会议电话等。这类应用程序需要能够快速响应来自用户或系统的事件,保证通信系统的高效运作。为了更容易理解JAIN-SLEE的架......
  • gitlab安装好后无法登录,如何修改密码
    部署一切正常的话,就可以访问gitlab的管理界面了。端口可以在vim/etc/gitlab/gitlab.rb文件中修改。修改后记得使用命令:gitlab-ctlreconfigure刷新配置,然后重启gitlab,命令是gitlab-ctlrestart。既然是linux,管理员的账号一般都是root。开始整:进入路径:cd/opt/gitlab/bingi......