Java学习day28:线程池Pool(知识点非常非常的详解)

声明:该专栏本人重新过一遍java知识点时候的笔记汇总,主要是每天的知识点+题解,算是让自己巩固复习,也希望能给初学的朋友们一点帮助,大佬们不喜勿喷(抱拳了老铁!)


往期回顾

Java学习day27:join方法、生产者消费者模式(知识点详解)-CSDN博客

Java学习day26:和线程相关的Object类的方法、等待线程和唤醒线程(知识点详解)-CSDN博客

Java学习day25:守护线程、死锁、线程生命周期(知识点详解)-CSDN博客

 Java学习day28:线程池Pool

一、线程池Pool

1.什么是线程池

线程池是一个容纳了多个线程的容器,其中的线程可以反复的使用。省去了频繁创建线程的对象的操作,无需反复创建线程而消耗更多的资源

2.工作原理

 3.7种具体实现方法

在Java语言中,并发编程都是通过创建线程池来实现的,而线程池的创建方式也有很多种,每种线程池的创建方式都对应了不同的使用场景,总体来说线程池的创建可以分为以下两类: 

①通过 ThreadPoolExecutor 手动创建线程池。
②通过 Executors 执行器自动创建线程池。

 而以上两类创建线程池的方式,又有 7 种具体实现方法,每种实现方法都有不同特点,而实际开发基本用最后一种,前六种一半不用的,所以前面的大家以了解为主,但是也要知道,重点在于最后一个。

我们今天,先讲前六种,最后一种太重要知识点也太多了,我们单独拿出来讲。

1.FixedThreadPool(中文翻译就是,固定的线程池)

创建一个固定大小的线程池,可控制并发线程数。使用FixedThreadPool创建2个固定大小的线程池,执行任务的方法有两种:submit 和 execute,具体实现代码:

示例1:

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

public class Demo3 {
    public static void main(String[] args) {
        fixedThreadPool();
    }
    public static void fixedThreadPool() {
        // 创建 2 个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

        // 创建任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            }
        };

        // 线程池执行任务(一次添加 4 个任务)
        
        threadPool.submit(runnable);  // 执行方式 1:submit
        threadPool.execute(runnable); // 执行方式 2:execute
        threadPool.execute(runnable);
        threadPool.execute(runnable);
    }
}

示例2:

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

public class Demo2 {
    public static void main(String[] args) {

        //1.创建线程池 创建了两个线程
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

        //2.任务
       Runnable run1 =  new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("晚上吃饭。线程为:" + Thread.currentThread().getName());

                }
            }
        };
        Runnable run2 =  new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println("晚上睡觉。线程为:" + Thread.currentThread().getName());

                }
            }
        };
        Runnable run3 =  new Runnable() {
            @Override
            public void run() {
                //run方法中写的是需求!!
                for (int i = 0; i < 20; i++) {
                    System.out.println("晚上敲代码不睡觉。线程为:" + Thread.currentThread().getName());

                }
            }
        };
        //3.执行上上面的三个任务
        threadPool.submit(run1);
        threadPool.execute(run2);
        threadPool.execute(run3);

    }
}

 注意点:

Ⅰ.都可以概括为三个步骤:

①创建固定数量的线程池
②使用Runnable匿名内部类创建任务
③调用submit 或 execute执行任务

Ⅱ.run方法里面写的是任务需求,业务实现。

Ⅲ.记住,执行任务的时候,线程依旧是抢占式运行的。

2.Executors.newCachedThreadPool

创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。

示例

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

public class Demo3 {
    public static void main(String[] args) {
        cachedThreadPool();
    }
    public static void cachedThreadPool() {
        // 创建线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        // 执行任务
        for (int i = 0; i < 10; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                    }
                }
            });
        }
    }
}

使用场景:

CachedThreadPool 是根据短时间的任务量来决定创建的线程数量的,所以它适合短时间内有突发大量任务的处理场景。 

3.Executors.newSingleThreadExecutor

创建单个线程数的线程池,它可以保证先进先出的执行顺序。其底层实现是队列

示例:

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

public class Demo3 {
    public static void main(String[] args) {
        singleThreadExecutor();
    }

    public static void singleThreadExecutor() {
        // 创建线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        // 执行任务
        for (int i = 0; i < 10; i++) {
            final int index = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(index + ":任务被执行");
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                    }
                }
            });
        }
    }
}

单个线程的线程池有什么意义?单个线程的线程池相比于线程来说,它的优点有以下 2 个:

Ⅰ.可以复用线程:即使是单个线程池,也可以复用线程。 

Ⅱ.提供了任务管理功能:单个线程池也拥有任务队列,在任务队列可以存储多个任务,这是线程无法实现的,并且当任务队列满了之后,可以执行拒绝策略,这些都是线程不具备的。

4.Executors.newScheduledThreadPool:

创建一个可以执行延迟任务的线程池。

示例

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo3 {
    public static void main(String[] args) {
        scheduledThreadPool();
    }

    public static void scheduledThreadPool() {
        // 创建线程池
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
        // 添加定时执行任务(1s 后执行)
        System.out.println("添加任务,时间:" + new Date());
        threadPool.schedule(new Runnable() {
                                @Override
                                public void run() {
                                    System.out.println("任务被执行,时间:" + new Date());
                                    try {
                                        TimeUnit.SECONDS.sleep(1);
                                    } catch (InterruptedException e) {
                                    }
                                }
                            }
                , 3, TimeUnit.SECONDS);
    }

}

threadPool.schedule(1,2,3)里面的三个参数,1是需要执行的任务,2是延迟执行时间,3是延迟执行时间单位,这里是秒。

5.Executors.newSingleThreadScheduledExecutor

创建一个单线程的可以执行延迟任务的线程池。此线程池可以看作是 ScheduledThreadPool 的单线程池版本。 

示例

ublic static void SingleThreadScheduledExecutor() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
    // 添加定时执行任务(2s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 2, TimeUnit.SECONDS);
}
6.Executors.newWorkStealingPool

创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】

示例

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo3 {
    public static void main(String[] args) {
        workStealingPool();
    }

    public static void workStealingPool() {
        // 创建线程池
        ExecutorService threadPool = Executors.newWorkStealingPool();
        // 执行任务
        for (int i = 0; i < 10; i++) {
            final int index = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
                }
            });
        }
        // 确保任务执行完成
        while (!threadPool.isTerminated()) {
        }
    }
}

 从上述结果可以看出,任务的执行顺序是不确定的,因为它是抢占式执行的。

 7.ThreadPoolExecutor

手动创建线程池的方式,它创建时最多可以设置7个参数。(开发中用,面试要考)这一个非常非常重要,我们拿出来,单独用一篇文章讲。

4.总结

线程池的创建方式总共有以下 7 种:

1. Executors.newFixedThreadPool创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
2. Executors.newCachedThreadPool创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
3. Executors.newSingleThreadExecutor创建单个线程数的线程池,它可以保证先进先出的执行顺序。
4. Executors.newScheduledThreadPool创建一个可以执行延迟任务的线程池。
5. Executors.newSingleThreadScheduledExecutor创建一个单线程的可以执行延迟任务的线程池。
6. Executors.newWorkStealingPool创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】
7. ThreadPoolExecutor手动创建线程池的方式,它创建时最多可以设置 7 个参数。

而**线程池的创建推荐使用最后一种 ThreadPoolExecutor 的方式来创建,因为使用它可以明确线程池的运行规则,规避资源耗尽的风险**。


以上,就是今天的所有知识点了。线程池是Java中非常非常重要的核心部分,前六种具体创建线程池的方式,即使开发用的不多,大家也需要了解掌握,大家要自己多花点时间,静下心看代码,写代码,多理解,多运用,重点是多去运用。

加油吧,预祝大家变得更强!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值