线 程 池

目录

目录

一.工厂模式

二.线程池

1.创建线程池

变量捕获

2.ThreadPoolExecutor类

a.构造方法

b.拒绝策略

3、实现一个简单的线程


介绍线程池之前,我们先了解一下一中设计模式---工厂模式。

一.工厂模式

        工厂模式是用普通方法来代替构造方法,以此来更加灵活的构造不同的实例。那么为什么构造方法无法实现呢?因为构造方法存在一定的局限性。如果使用同一个参数列表的构造方法,那么就只能构造出一种实例,无法满足实际需求了。工厂模式解决了这种问题,它使用普通方法来构造实例,所以我们要创建何种实例就使用不同的方法来构造就行了。

        👇举一个简单的栗子,来更好的理解工厂模式的作用。

class student{
    String name;
    int age;
}
class teacher{
    String name;
    int age;
}
class personFactory{
    public static student newStudent(String name,int age){//构造学生
        //........
    }
    public static teacher newTeacher(String name,int age){//构造老师
        //........
    }
}

        在上面的代码中,我们通过两个静态方法构造了学生实例和老师实例,这两个方法的参数完全一致,如果我们使用构造方法,是无法完成这样的构造的。

        其实这样一看,工厂模式也并不是什么高深莫测的东西,它更像是规范了一种代码写法,使之规避语法上的缺陷。

        介绍完了工厂模式,我们来了解线程池。


二.线程池

        线程池的存在原因是随着技术的发展,普通创建线程的效率已经无法满足需求了,此时需要提高创建和销毁线程的效率。由于Java标准库还没有协程(一种轻量级线程),所以目前只能使用线程池来提高效率。

        此处的线程池本质与字符串常量池,数据库连接池一样,它把将使用的线程,提前创建好,并放在线程池中,当需要使用线程时,直接从线程池中获取,使用完成后,还给线程池。向池获取线程以及把线程还给池,这两个动作(用户代码实现,无需交由内核完成)比创建线程、销毁线程高效许多,这样就提高了程序的效率。

1.创建线程池

Java标准库中提供了Executors工厂类创建线程池。

public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(10);//使用工厂类创建线程
        for (int i = 0 ;i <1000;i++) {
            int n = i;
            pool.submit(new Runnable() {//将任务加入到线程池中
                @Override
                public void run() {
                    System.out.println(n);
                }
            });
        }
    }

线程池的线程数设定规则:需要共同考虑程序对CPU的占用和IO的占用,来设定合适地线程数。

具体做法:通过测试确定。

变量捕获

        在上面代码的for循环中,为什么输出的不可以是i而是n?

        i是主线程上的变量,存在于主线程栈上,但是在多线程的环境下,submit把任务加入到线程池执行,任务实际的执行时间是不确定的,可能轮到任务执行时,i已经改变了许多次甚至已经销了,所以java有变量捕获这个技能,其作用是在run被定义的时候,就把主线程栈上的i在当前run的栈上拷贝一份,后面在执行run时,就使用拷贝的i。在JDK1.8前,要求变量捕获必须是final修饰的变量,在JDK1.8开始,放松了标准,要求不一定要被final修饰,只要代码没有修改过这个变量,也可以捕获


2.ThreadPoolExecutor类

上面的工厂Executors类总共用以下方法构造线程池。

而在这些方法中除了newWorkStealingPool外,都是通过ThreadPoolExecutor来实现的。

如newCachedThreadPool的源码所示: 

a.构造方法

那就来看看ThreadPoolExecutor的构造方法: 

第四个构造方法包含了前三种,我们就介绍第4种的参数列表。(面试高频)

1️⃣这些构造方法中的前两个参数,corePoolSize代表了核心线程数maximumPoolSize代表了最大线程数。举个例子理解的话就是:核心线程数就是一个公司的正式员工,最大线程数是一个公司的实习生,那么根据公司的情况,采用一种调整策略:正式员工保持不动,根据任务的多少,动态调节临时工的数量

2️⃣keepAliveTime是规定非核心线程在这一个时间内都没有任务做,就会被销毁,TimeUnit则是时间单位参数。

3️⃣BlockingQueue<Runnable>为线程池的任务队列,如果有任务,线程池非配线程工作,如果没有任务就阻塞。

4️⃣ThreadFactory用于创建线程。

5️⃣RejectExecutionHandler描述了一种拒绝策略,如果当队列满时,继续添加任务会有什么样的行为。

b.拒绝策略

第一种:如果队列满了,直接抛出异常。

第二种:如果队列满了,多出来的任务,那个线程加的,谁执行。

第三种:如果队列满了,丢弃最早的任务。

第四中:如果队列满了,丢弃最新的任务。

3、实现一个简单的线程池

class MyThreadPool {
    private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>();

    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while(true){
                    try {
                        Runnable runnable = blockingQueue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }

    public void submit(Runnable runnable) {
        try {
            blockingQueue.put(runnable);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

以上就是本文的全部内容了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

todd1er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值