十、线程池

1、什么是线程池

线程池的概念可以与现实生活中的某些场景进行类比,比如一家餐厅的服务模式。让我们来详细看看这个例子:

想象一下,有一家繁忙的餐厅,顾客(任务)不断进入餐厅点餐。每一张桌子都有一个服务员(线程)负责接待和服务。如果每次有新顾客到来,餐厅都雇佣新的服务员,那么很快就会导致成本过高,并且管理这些服务员也会变得非常复杂。

为了更高效地服务顾客,餐厅决定采用一组固定数量的服务员(线程池)。这组服务员在没有顾客时处于待命状态。当有新顾客到来时,任意一个空闲的服务员会立即上前服务这位顾客。一旦顾客被服务完毕离开餐厅,该服务员又回到待命状态,准备服务下一位顾客。

这种安排有几个好处:

  1. 减少了频繁雇佣和解雇服务员(创建和销毁线程)的开销。
  2. 服务员(线程)能够更快地上前服务顾客(执行任务),因为他们已经准备好并且熟悉了工作流程。
  3. 管理更容易,因为只需要管理有限数量的服务员。

因此,通过维持一组预先创建的服务员(线程池),餐厅能够更加高效、经济地处理顾客流量(任务负载)。

2、标准库中的线程池

使⽤Executors.newFixedThreadPool(10)能创建出固定包含10个线程的线程池.

• 返回值类型为ExecutorService

• 通过ExecutorService.submit可以注册⼀个任务到线程池中.

package thread_demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Demo34 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 1000; i++) {
            final int id = i;
            threadPool.submit(() -> {
                System.out.println("hello" + id + "," + Thread.currentThread().getName());
            });
        }
    }
}

Java标准库,提供了一组类,针对ThreadPoolExecutor进行了进一步封装,简化线程池的使用,也是基于工厂设计模式。

Executors 创建线程池的⼏种⽅式

newFixedThreadPool创建固定线程数的线程池
newCachedThreadPool创建线程数⽬动态增⻓的线程池
newSingleThreadExecutor创建只包含单个线程的线程池.
newScheduledThreadPool设定延迟时间后执⾏命令,或者定期执⾏命令.是进阶版的Timer. Executors 本质上是ThreadPoolExecutor类的封装

Executors 本质上是ThreadPoolExecutor类的封装

ThreadPoolExecutor 提供了更多的可选参数,可以进⼀步细化线程池⾏为的设定

线程池=> 公司

核心线程=> 正式员工

非核心线程 => 实习生

int corePoolSize:核心线程数  

至少有多少个线程,线程池一创建,这些线程也要随之创建.直到整个线程池销毁,这些线程才会销毁

int maximumPoolSize:最大线程数  

核心线程+非核心线程 不繁忙就销毁,繁忙就再创建

long keepAliveTime:非核心线程允许空闲的最大时间

允许实习生摸鱼的时间~~

如果实习生连续一个月都没有啥活了,就可以考虑优化掉

TimeUnit unit :keepaliveTime 的时间单位

秒,分钟,还是其他值

 BlockingQueue<Runnable>:工作队列

传递任务的阻塞队列

 ThreadFactor:工厂模式

统一的构造并初始化线程

创建线程的⼯⼚,参与具体的创建线程⼯作.通过不同线程⼯⼚创建出的线程相当于 对⼀些属性进⾏了不同的初始化设置.

工厂模式:

一种设计模式,和单例模式是并列的关系

用来弥补构造方法的缺陷的

请看下例:

Java有的一个问题,构造方法的名字是固定的,要想提供不同的版本,就需要通过重载,有时候不一定能构成重载。因此就需要工厂模式。

工厂方法的核心:

通过静态方法,把构造对象new的过程,各种属性初始化的过程,封装起来了,提供多组静态方法,实现不同情况的构造。

package thread_demo;

//工厂模式
class Point {

    public static Point makePointByXY(double x, double y) {
        Point p = new Point();
        return p;
    }

    public static Point makePointByRA(double r, double a) {
        Point p = new Point();
        return p;
    }
}

public class Demo33 {
    public static void main(String[] args) {
        Point p1 = Point.makePointByXY(10, 20);
        Point p2 = Point.makePointByRA(30, 40);
    }
}

RejectedExecutionHandler:拒绝策略

如果任务量超出公司的负荷了接下来怎么处理

ThreadPoolExecutor.AbortPolicy线程池直接抛出异常 (线程池就可能无法继续工作)
ThreadPoolExecutor.CallerRunsPolicy让调用submit的线程自行执行任务.
ThreadPoolExecutor.DiscardoldestPolicy丢弃队列中最老的任务
ThreadPoolExecutor.DiscardPolicy丢弃最新的任务,当前submit的这个任务.

举出一个生活中的例子:

  1. AbortPolicy:想象你在一个繁忙的餐厅,服务员已经满负荷工作,无法再接受新的订单。如果这时你试图下单,服务员会直接告诉你他们无法接受新的订单,并让你离开。这就像线程池抛出异常,表示无法处理更多的任务。

  2. CallerRunsPolicy:还是在餐厅的例子中,服务员已经满负荷工作,无法接受新的订单。但是,如果你坚持要下单,服务员会建议你自己去厨房准备食物。这就像调用submit方法的线程直接执行任务,而不是将任务放入线程池。

  3. DiscardOldestPolicy:继续餐厅的例子,服务员已经满负荷工作,无法接受新的订单。但是,他们决定取消最旧的订单,然后接受你的新订单。这就像线程池丢弃队列中最旧的任务,然后尝试再次提交新任务。

  4. DiscardPolicy:最后,还是在餐厅的例子中,服务员已经满负荷工作,无法接受新的订单。如果你试图下单,服务员会直接告诉你他们无法接受新的订单,并让你离开,但不会给出任何反馈。这就像线程池直接丢弃新提交的任务,不会抛出异常,调用者不会得到任何反馈。

3、实现线程池

• 核⼼操作为submit,将任务加⼊线程池中

• 使⽤Worker类描述⼀个⼯作线程.使⽤Runnable描述⼀个任务.

• 使⽤⼀个BlockingQueue组织所有的任务

• 每个worker线程要做的事情:不停的从BlockingQueue中取任务并执⾏.

• 指定⼀下线程池中的最⼤线程数maxWorkerCount;当当前线程数超过这个最⼤值时,就不再新增 线程了

package thread_demo;


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;


//实现线程池
class MyThreadPool{
    private BlockingQueue<Runnable> queue =null;
    public MyThreadPool(int n){
        //初始化线程池,创建固定个数线程
        queue=new ArrayBlockingQueue<>(1000);

        //创建N个线程
        for (int i = 0; i <n ; i++) {
            Thread t=new Thread(()->{
                while (true){
                    try {
                       Runnable task = queue.take();
                       task.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

            t.start();
        }
    }

    public void sumbit(Runnable task) throws  InterruptedException{
        queue.put(task);
    }
}


public class Demo35 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool threadpool =new MyThreadPool(10);
        for (int i = 0; i <100 ; i++) {
             int id =i;
            threadpool.sumbit(()->{
                System.out.println(Thread.currentThread().getName()+"i="+id+"running");
            });
        }



    }
}

运行代码:

程序并未终止,线程池里的这些线程,还再take阻塞的.(等待)
线程池中的线程,是前台线程,阻止进程结束!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值