简单认识线程池

简单认识线程池

1 简介

对于传统使用多线程的模式会有以下问题:

多线程运行时间,系统不断的启动和关闭新线程,会过渡消耗系统资源

过度切换线程的危险,从而可能导致系统资源的崩溃

池化技术

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

以前是直接把任务交给具体的资源,而现在把资源交给池,池就会让空闲的资源去执行任务,任务执行完了以后,资源并不会销毁,而是停留池里面,等待下一个任务来执行

线程池好处

降低系统资源消耗、方便线程并发数的管控、可以延时定时线程池

2 工作原理

在这里插入图片描述

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

线程池中的线程不需要start,只要提交(execute)上去,线程池会在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程,如果可执行的线程已经满了,那么剩下的任务会进入等待,直到其中某个线程执行结束将线程让出。

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

3 工作队列

BlockingQueue

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

public interface BlockingQueue<E> extends Queue<E>

由于继承了队列接口,所以遵循了队列的原则,即先进先出(FIFO)阻塞队列继承了Queue队列遵循先进先出原则 队列提供几种基本的操作: 添加元素(队尾) 移除元素(队头) 取出队头元素(不移除) 每种操作都有两个方法,一种有可能抛异常,一种返回操作成功或 失败。 其实现类如下:

ArrayBlockingQueue 采用数组实现,capacity是构建时传入的参数,表示生成的BlockingQueue的大小,其所含的对象是FIFO排序

public ArrayBlockingQueue(int capacity) {
    this(capacity, false);
}

LinkedBlockingQueue 采用双向链表实现,大小不固定的。如果用无参构造,则默认大小是Integer.MAX_VALUE,若传入capacity且capacity合法,则自定义生成的BlockingQueue的大小,其所含的对象是FIFO排序

public LinkedBlockingDeque() {
    this(Integer.MAX_VALUE);
}
public LinkedBlockingDeque(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
}

PriorityBlockingQueue 类似于LinkedBlockingQueue,不过所含对象不是FIFO排序,而是依据对象的自然顺序或者构造函数的Comparator决定。

public PriorityBlockingQueue() {
    this(DEFAULT_INITIAL_CAPACITY, null);
}

SynchronousQueue 是一种特殊的BlockingQueue,这个容器相当于通道,本身不存储元素,每次想要进行删除操作都要先有插入操作,每次想要进行插入操作都要先有等待删除操作,在多任务队列,这是最快的处理任务方式

public SynchronousQueue() {
    this(false);
}

4 线程池的使用

创建对象:ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,//核心线程数
                          int maximumPoolSize,//最大线程数(即核心线程数+其他线程数的总和)
                          long keepAliveTime,//除了核心线程之外其他线程的最长存活时间
                          TimeUnit unit,//时间单位
                          BlockingQueue<Runnable> workQueue) {//工作队列
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
//基本练习
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,6,2, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
//提交任务去执行
Runnable t1 = ()-> System.out.println(Thread.currentThread().getName());
Runnable t2 = ()-> System.out.println(Thread.currentThread().getName());
Runnable t3 = ()-> System.out.println(Thread.currentThread().getName());
executor.execute(t1);
executor.execute(t2);
executor.execute(t3);
//运行到此处main线程不会关闭,线程池中还有两个核心线程没有执行结束

//关闭线程池,等到里面所有的线程空闲了才会关闭
//如果有线程正在执行,那就等待执行完毕再关闭
executor.shutdown();
//showdown底层使用interrupt中断线程

5 常用线程池

java通过Executors提供了四种线程池供我们直接使用:

newCachedThreadPool

​ 重复使用线程,一旦线程空闲,就不会创建,而是使用上一个任务遗留的线程执行

​ 由源码可知:创建的都是非核心线程,最大线程数为Integer.MAX_VALUE,空闲线程存活时间是1分钟

​ 有大量任务需要执行的情况下用这个

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

newFixedThreadPool

​ 该线程池一旦创建,就会创建指定size的核心线程

​ 由源码可知:核心线程数和最大线程相同,且空闲线程存活时间为0L

​ 可以固定任务数量的情况下用这个

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

newSingleThreadExecutor

​ 线程池里只创建了一个线程,这样就可以保证按照顺序执行任务

​ 由源码可知:该线程池只创建了一个核心线程,且最大线程数为 1,空闲线程存活时间为 0L

​ 有需求顺序执行任务时用这个

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

newScheduledThreadPool

​ 增加了延迟执行,和周期性执行

​ 由源码可知:创建的是ScheduledThreadPoolExecutor,返回ScheduledExecutorService对象,向上追溯源码可知,创建了所传参数个核心线程,最大线程数是Integer.MAX_VALUE,空闲线程存活时间为 0

​ 有需求周期性执行任务时用这个

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}//super是ThreadPoolExecutor

​ 测试延迟执行和周期性执行的功能

ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
Runnable runnable = ()-> System.out.println(Thread.currentThread().getName());
//正常执行
service.execute(runnable);
//延迟执行:第二个和第三个参数分别为延迟时间和单位
service.schedule(runnable,3, TimeUnit.SECONDS);
//周期性执行:第2-4参数分别为:延迟执行时间,周期性执行时间,时间单位
service.scheduleAtFixedRate(runnable,1,2,TimeUnit.SECONDS);

6 小结

​ 对线程池的初步了解就到这里,具体使用哪一个线程池还是得看具体需求,后期随着学习的深入再慢慢了解线程池的原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadPoolExecutor 是 Java 中的一个线程池实现,它提供了一种管理线程的机制,可以有效地控制线程的数量,避免因为线程过多而导致系统资源的浪费和性能下降。 ThreadPoolExecutor 的主要构造函数如下: ``` public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) ``` 其中,各个参数的含义如下: - `corePoolSize`:核心线程数,即线程池中保持的最少线程数。 - `maximumPoolSize`:线程池所能容纳的最大线程数。 - `keepAliveTime`:线程池中超过 `corePoolSize` 的空闲线程能够存活的最长时间。 - `unit`:`keepAliveTime` 的时间单位。 - `workQueue`:任务队列,用于保存等待执行的任务。 - `threadFactory`:线程工厂,用于创建新线程。 - `handler`:拒绝策略,用于当任务队列满了且当前线程数已达到最大线程数时如何处理新任务。 ThreadPoolExecutor 在初始化时会创建 `corePoolSize` 个线程,并将剩余的任务添加到任务队列 `workQueue` 中。当任务队列满了时,如果当前线程数小于 `maximumPoolSize`,则会创建新的线程来执行任务;如果当前线程数已达到最大线程数,则会根据拒绝策略 `handler` 来处理新任务。 ThreadPoolExecutor 还提供了一些方法,如 `execute()`、`submit()`、`shutdown()` 等,用于提交任务、关闭线程池等操作。需要注意的是,当使用完线程池后,应该及时调用 `shutdown()` 方法来关闭线程池以释放资源。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值