线程池初步认识

1.什么是线程池
为了避免系统频繁地创建和销毁线程,消耗系统资源,我们可以让创建的线程进行复用。就如同数据库的连接池一样,当系统需要用到数据库时,并不是创建一个新的连接,而是从连接池中获取一个连接。线程也类似,当需要用到线程时就可以去池子中拿一个空闲线程,用完之后就“还”给线程池,通过这种方式可以节约不少创建和销毁线程的时间。
2.JDK对线程池的支持
Executor结构图:
这里写图片描述
其中ThreadPoolExecutor表示一个线程池,Executors类则扮演这线程池工厂的角色。通过这个工厂可以获得一下类型的线程池:

public static ExecutorService newFixedThreadPool(int nThreads);
public static ExecutorService newSingleThreadExecutor();
public static ExecutorService newCachedThreadPool();
public static ScheduledExecutorService newSingleThreadScheduledExecutor();
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);

1.newFixedThreadPool()方法:该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有则新的任务会被暂存在一个任务队列中,待有线程空闲时在执行。
2.newSingleThreadExecutor()方法:该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
3.newCachedThreadPool()方法:该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
4.newSingleThreadScheduledExecutor()方法:该方法返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExecutorService接口在ExecutorService接口上扩展了在给定时间执行某任务的功能。
5.newScheduledThreadPool()方法:和newSingleThreadScheduledExecutor()方法不同的是该线程池可以指定线程数量。
2.1计划任务
与其他几个线程池不同,ScheduledExecutorService并不会一定会立即安排执行任务。它其实是起到了计划任务的作用。它提供了一下几个方法

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
public ScheduledFuture<?> shceduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

方法schedule()会在给定时间,对任务进行一次调度。方法shceduleAtFixedRate和方法scheduleWithFixedDelay会对任务进行周期性调度。对于FixedRate来说,任务调度的频率是一定的,它是以上一个任务开始执行时间为起点,之后的period时间调度下一次任务。而FixDelay则是在上一个任务结束后,再经过Delay时间进行任务调度。
2.2核心线程池的内部实现
线程池均使用了ThreadPoolExecutor实现。以下是三个线程池的实现方法:

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

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

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

从以上代码实现来看,他们只是ThreadPoolExecutor。下面是其最重要的构造函数

 public ThreadPoolExecutor(int corePoolSize,  //指定线程池中的线程数量
                              int maximumPoolSize,  //指定线程池中的最大线程数量
                              long keepAliveTime,  //当线程数量超过corePoolSize时,多余空闲线程的存活时间
                              TimeUnit unit,  //存活时间的单位
                              BlockingQueue<Runnable> workQueue,  //任务队列,被提交但尚未被执行的任务
                              ThreadFactory threadFactory,  //线程工厂,用于创建线程,一般为默认
                              RejectedExecutionHandler handler);  //拒绝策略,当任务太多来不及处理,如何拒绝任务

参数workQueue任务队列,是一个BlockingQueue接口的对象,用于存放Runnable对象。根据队列功能的分类,可使用以下几种BlockingQueue:
1.直接提交队列:由SynchronousQueue对象提供。它是一种特殊的BlockingQueue。SynchronousQueue没有容量,每一个插入操作对应着一个删除操作,反之也一样。提交的任务不会被真实保存,而是将新任务提交给线程执行。
2.有界任务队列:可以使用ArrayBlockingQueue实现。若有新的任务需要执行时,如果线程池的实际线程数小于CorePoolSize,则会优先创建新的线程,若大于CorePoolSize,则会将新的任务加入等待队列。若队列已满,无法加入,则在总线程数不大于maximumPoolSize的前提下,创建新的进程执行任务。若大于,则执行拒绝策略。
3.无解任务队列:通过LinkedBlockingQueue类实现。与有界队列相比,除非资源耗尽,否则无界的任务队列不存在任务入队失败的情况。
4.优先任务队列:优先任务队列是带有执行优先级的队列。通过PriorityBlockingQueue实现,可以控制任务的执行先后顺序,是一种特殊的无解队列。
2.3.1拒绝策略
ThreadPoolExecutor的最后一个参数是拒绝策略。当系统超负荷运行时就会执行拒绝策略,JDK内置的拒绝策略如下:
1.AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
2.CallerRunsPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是任务提交线程的性能极有可能会急剧下降。
3.DiscardOledestPolicy策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
4.DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。
以上内置的策略均实现的RejectedExecutionHandler接口,当然也可以自己扩展接口,自定义自己的策略。接口定义如下:

public interface RejectedExecutionHandler{
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

2.3.2自定义线程创建工厂:ThreadFactory
ThreadFactory是一个接口,只有一个方法用于创建线程,我们可以实现这个接口,自定义线程创建工厂:

Thread newThread(Runnable r);

3.扩展线程池
ThreadPoolExecutor是一个可以扩展的线程池。它提供了beforExecute(),afterExecute(),terminated()三个接口线程池进程扩展,线程池扩展对线程池运行状态的跟踪,输出一些有用的调试信息以帮助系统故障诊断,如:

package test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ExtThreadPool {
    public static class MyTask implements Runnable{
        public String name;

        public MyTask(String name) {
            // TODO Auto-generated constructor stub
            this.name =  name;
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println("正在执行"+":Thread Id:"+Thread.currentThread().getId()+",Task Name="+name);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, 
                new LinkedBlockingQueue<Runnable>())
                {
                    @Override
                    protected void beforeExecute(Thread t, Runnable r) {
                        System.out.println("准备执行: "+((MyTask)r).name);
                    }

                    @Override
                    protected void afterExecute(Runnable r, Throwable t) {
                        System.out.println("执行完成: "+((MyTask)r).name);
                    }

                    @Override
                    protected void terminated() {
                        System.out.println("线程退出");
                    }
                };

                for(int i=0; i<5; i++)
                {
                    MyTask task = new MyTask("TASK-GEYM-"+i);
                    es.execute(task);
                    Thread.sleep(10);
                }
                es.shutdown();

    }

}

4.优化线程池线程数量
计算公式如下:
这里写图片描述

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、付费专栏及课程。

余额充值