Java线程池简单梳理

tech2024-06-17  68

1.简介

传统多线程带来的问题:

多线程运行时间,系统不断的创建,销毁线程带来额外的开销。过度切换线程的危险,从而导致系统资源的崩溃

线程池的好处:

降低系统资源消耗,通过重用已存在的线程,降低线程创建的开销方便线程并发数的管控提供更强大的功能,延时定时线程池。

池化技术:

​ 不直接创建具体的资源,而是创建一个池,在池里面创建具体的资源。

​ 以前是直接控制资源去执行任务,现在是把任务交给池,池就会让空闲的资源去执行任务,

​ 任务完成后,资源不会被销毁,而是停留在 池里面,等待下一个任务。

2. 工作原理

在线程池的编程模式下。

任务是提交给整个线程池,线程池在拿到任务后,就在内部寻找空闲的线程执行任务。

一个线程只能执行一个任务,但可以同时向一个线程池提交多个线程池。

线程池工作的本质就是将要执行的任务添加到队列中,然后线程池寻找空闲的线程来执行队列中空闲的任务。

3.线程池介绍

3.1 工作队列

java中主要采用BlockingQueue去存储任务

3.1.1 接口BlockingQueue

双缓冲队列,内部使用两条队列,允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提

高了队列的存储效率。

3.1.2 实现类ArrayBlockingQueue

采用数组实现,规定大小,采用FIFO的顺序存储数据。

3.1.3 实现类LinkedBlockingQueue

采用双向链表实现,大小不固定。若不指定大小,大小由Integer.MAX_VALUE指定,存储顺序为FIFO。

3.1.4 实现类SynchronousQueue

特殊的BlockingQueue每次删除操作都要等待插入操作每次插入操作都要等待删除操作一个元素,一旦有了插入线程和移除线程,那么很快由插入线程移交给移除线程。这个容器相当于通道,本身不存储数据在多任务队列,是最快的处理任务方式。

3.2创建线程池

java中使用ThreadPoolExecutor来表示线程池,创建该类对象来表示创建一个线程池

new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)

corePoolSize 核心线程数,创建线程池就会创建的线程数,核心线程不会被回收

maximumPoolSize 最大线程数,除了核心线程外还会存在临时线程,最大线程数是核心线程+临时线程的最大允许数。

keepAliveTime 线程池中除了核心线程之外的其他线程存活时间

unit 时间单位 TimeUnit中提供了几个枚举类 有SECONDS 、MINUTES、HOURS、DAYS

workQueue 工作队列 通常使用 SynchronousQueue。

实例:

new ThreadPoolExecutor(2,6,2, TimeUnit.SECONDS,new SynchronousQueue<>());

3.3使用线程池

通过线程池的execute方法执行任务

例子:

ublic static Boolean flag = true; public static void main(String[] args) { Runnable testA = new Runnable() { @Override public void run() { synchronized (Object.class){ for (int i=1;i<=100;i++) //flag为true时可以执行 if (flag){ System.out.println("A"+i); try { //任务执行完毕后 反转标志 并叫醒另一个线程,自己进入等待 flag=!flag; Object.class.notify();//争夺什么锁资源就在什么锁资源上等待或者唤醒 Object.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; Runnable testB = new Runnable() { @Override public void run() { synchronized (Object.class){ for (int i=1;i<=100;i++) if (!flag){ System.out.println("B"+i); try { flag=!flag; Object.class.notify(); Object.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; ThreadPoolExecutor ex= new ThreadPoolExecutor(4,5,6, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); ex.execute(testA); ex.execute(testB);

4.常用线程池

4.1newCacheThreadPool

可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务

例子:

//重复使用线程,只要线程空闲就不会创建新的线程,而是使用上一个任务遗留的线程去执行任务。 //线程池尽量不创建新的线程,只有迫不得已才会创建新的线程,最大线程数是Integer.MAX_VALUE(2147483647) //没有核心线程,等到60秒后进程会自动关闭 ExecutorService exe= Executors.newCachedThreadPool(); for (int i=0;i<20;i++){ //Thread.sleep(2); exe.execute(()-> System.out.println(Thread.currentThread().getName())); }

4.2 newFIxedThreadPool

创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。

例子:

//输入参数是多少 创建的线程池的核心线程数量就是多少 //线程池一旦创建,就会创建指定size的核心线程,jvm不会自动关闭 //线程池的最大线程数也是核心线程数,不存在临时线程。 ExecutorService ex =Executors.newFixedThreadPool(5); for (int i=1;i<=20;i++){ ex.execute(()-> System.out.println(Thread.currentThread().getName())); } //查看电脑最大允许核心线程数,若超过则无法创建 System.out.println(Runtime.getRuntime().availableProcessors());

4.3 newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

//线程池里面只创建了一个线程,这样就可以保证按顺序去执行任务 ExecutorService ex= Executors.newSingleThreadExecutor(); for (int i=1;i<=10;i++){ ex.execute(()-> System.out.println(Thread.currentThread().getName())); }

4.4 newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行

//增加了延迟执行和周期性执行 ScheduledExecutorService ex= Executors.newScheduledThreadPool(5); //延迟执行 //参数列表:Runnable command, long delay, TimeUnit unit ex.schedule(()-> System.out.println("hello"),3,TimeUnit.SECONDS); //周期性执行 第一个参数为Runnable,第二个参数为初始延迟,第三个重复执行间隔,第四个为时间单位 //参数列表Runnable command, long initialDelay, long period, TimeUnit unit ex.scheduleAtFixedRate(()-> System.out.println("world"),1,2,TimeUnit.SECONDS);
最新回复(0)