目录
1. 线程池
2. 创建线程池
2.1 Executors类
2.2 ThreadPoolExecutor类
3. 自己实现线程池
1. 线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动.
1.那么为什么要有线程池呢?
通俗的来讲,就是将线程提前准备好,创建线程不是直接从系统进行申请,而是从线程池里面拿,等待线程不用了,就放回池里.
2.为什么从线程池拿线程要比创建线程快呢?
这就涉及到用户态和内核态
举例:
2. 创建线程池
2.1 Executors类
这是标准库中提供的创建线程池的方法
- 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
- 返回值类型为 ExecutorService
- 通过 ExecutorService.submit 可以注册一个任务到线程池中.
public class ExecutorsTest {public static void main(String[] args) {// 创建线程池,里面存放10个线程ExecutorService pool = java.util.concurrent.Executors.newFixedThreadPool(10);// 使用submit往线程池中提交若干的任务for (int i = 1; i <= 10; i++) {int number = i;pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello"+number);}});}}
}
Executors 创建线程池的几种方式
- newFixedThreadPool: 创建固定线程数的线程池
- newCachedThreadPool: 创建线程数目动态增长的线程池.
- newSingleThreadExecutor: 创建只包含单个线程的线程池.
- newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
Executors 本质上是 ThreadPoolExecutor 类的封装.
2.2 ThreadPoolExecutor类
拒绝策略
最老的这个任务就是最先安排的任务.
代码示例:
public class threadPoolExecutorTest {public static void main(String[] args) {ExecutorService pool = new ThreadPoolExecutor(1,3,1000, TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 3; i++) {pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});}}
}
3. 自己实现线程池
- 1. 使用堵塞队列来组织任务
- 2.实现一个固定线程数数量的线程池
- 3.使用submit方法,来往堵塞队列添加任务.
package ThreadPool;import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;/*** Created with IntelliJ IDEA.* Description:手动创建线程池* User: YAO* Date: 2023-05-18* Time: 15:21*/
public class MyThreadPool {// 1.内部使用阻塞队列实现,创建阻塞队列存放执行的任务private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();// 2.创建往线程池推送出任务的方法public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}//3. 创建构造方法,参数为线程的数量public MyThreadPool(int n){for (int i = 0; i < n; i++) {// 3.1 创建线程Thread t = new Thread(()->{try {while (true){// 此处需要有一个循环,就是无休止的取出,有任务就取出Runnable runnable = queue.take();runnable.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}});// 3.2 不要忘记启动线程t.start();}}
}
测试自己创建的线程池
package ThreadPool;/*** Created with IntelliJ IDEA.* Description:测试自己构建的线程池* User: YAO* Date: 2023-05-18* Time: 15:29*/
public class MyThreadPoolTest {public static void main(String[] args) throws InterruptedException {// 1. 创建线程池实例MyThreadPool pool = new MyThreadPool(10);// 问题2: 当前代码,搞了一个10个线程的线程池,实际开发中,一个线程池的线程设置为多少合适?//答案:跟CPU有关系,并不是线程越多越好.不同的程序,线程的工作是不一样的.// 1.CPU密集型:主要做一些计算,要在CPU上运行.(此时线程数不应该超过CPU的核心数)// 2.IO密集型任务:主要等待IO操作(等待读写硬盘,读写网卡),不咋吃CPU(此时线程数可以远远超过CPU的核心数)// 2. 往线程池添加任务for (int i = 0; i < 1000; i++) {int number = i;// 问题1: 为什么需要使用Number进行接收i的值?// 答案:lambda表达式(本质就是匿名内部类的写法)具有变量捕获,匿名内部类也有变量捕获!!!!// 此处创建Number接收i的值,每一次循环都会创建Number一次,也就是在这个循环里面Number就是不变的,进行下一次循环就重新创建Numberpool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello" + number);}});}Thread.sleep(1000);}
}
运行结果
1000个任务传入到阻塞队列,被此线程池的10个线程跑完了.