线程池

简介:

线程池是并发程序中的一个非常重要的技术和概念。线程池就是一个线程的池子,里面有若干线程,它们的目的是执行提交给线程池的任务,执行完一个任务后不会退出,而是继续等待或执行新任务。线程池是有两个概念组成,一个是任务队列,一个是工作者线程。工作者线程主题是一个循环,循环冲任务队列中接受任务并执行,任务队列保存待执行的任务。

线程池理解:

线程池的优点:
(1)它可以重用线程,减少创建线程的开销。
(2)任务过多时,通过排队避免创建过多的线程,减少系统资源的消耗和竞争,确保任务有序完成。

//线程池类的构造方法,下面我们来看下这些参数的作用。
/**
(1)corePoolSize:核心线程数,核心线程是不会退出的。当线程池创建时,并不会
立即创建所有的核心线程,而是等待有新任务来时,且当前线程池内的线程数小于核
心线程数,才会创建一个新的线程。
(2)maximumPoolSize:表示线程池中最多的线程数。当有新任务来时,线程池内的线程数大于核心线程数,就会尝试排队,但是当队列满时或者其他原因不能入队时,它就不会阻塞排队了,而是检查当前线程数是否达到了最大,如果没有,则创建新线程,直到达到最大线程数。
(3)keepAliveTime 和TimeUnit:空闲线程的存活时间。当线程池的线程大于核心线程数时,非核心线程会有一个等待任务的最长时间,到达最长时间没有新任务,则会被终止。KeepAliveTime 为0时表示所有线程不会被超时终止。
(4)BlockingQueue<Runnable>:工作队列。线程池要求的队列都是阻塞队列BlockingQueue。
.LinkedBlockingQueue:基于链表的阻塞队列,可以指定最大长度,但默认是无界的。
.ArrayBlockingQueue:基于数组的有界阻塞队列。
.priorityBlockingQueue:基于堆的无界阻塞优先级队列。
.SynchronousBlockingQueue:没有实际存储空间的同步阻塞队列。‘
如果使用的是无界队列,那么线程池中的线程数最大只能到达corePoolSize,因为是无界 的,所以新任务总是回去排队,所以maximumPoolSize就没意义了。
而SynchronousBlockingQueue,因为它没有实际的存储空间。当尝试排队时,只有正好有空闲线程在等待任务时才会入队成功。否则总是会创建新线程,直到线程数达到最大值。
(5)RejectedExecutionHandler:任务拒绝策略。如果队列有界,且maximumPoolSize有限,当队列排满,线程数达到最大,这是新任务来了就会触发任务拒绝策略。拒绝策略可以自定义,也可以使用给定的策略。
.ThreadPoolExecutor.AbortPolicy:这是默认的方式,抛出异常RejectedExecutionException。
.ThreadPoolExecutor.CallerRunsPolicy:静默处理,忽略新任务。不抛出异常,也不执行。
.ThreadPoolExecutor.DiscardOldestPolicy:将等待时间最长的任务扔掉,然后自己排队。
.ThreadPoolExecutor.DiscardPolicy:在任务提交者线程中执行任务,而不交给线程池中的线程执行。
(6)Thread Factory:线程工厂,用于创建线程。
*/
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
    }

线程池基础原理:

//线程池类
public class ThreadPoolExecutor extends AbstractExecutorService {
    //原子变量
    private final AtomicInteger ctl;
    private static final int COUNT_BITS = 29;
    private static final int COUNT_MASK = 536870911;
    private static final int RUNNING = -536870912;
    private static final int SHUTDOWN = 0;
    private static final int STOP = 536870912;
    private static final int TIDYING = 1073741824;
    private static final int TERMINATED = 1610612736;
    //阻塞队列
    private final BlockingQueue<Runnable> workQueue;
    //显示锁
    private final ReentrantLock mainLock;
    //工作者
    private final HashSet<ThreadPoolExecutor.Worker> workers;
    //显示条件
    private final Condition termination;
    //线程池创建的最大线程数
    private int largestPoolSize;
    private long completedTaskCount;
    //线程工厂
    private volatile ThreadFactory threadFactory;
    //任务拒绝策略
    private volatile RejectedExecutionHandler handler;
    //空闲线程存活时间
    private volatile long keepAliveTime;
    //是否运行核心线程也受存活时间参数影响
    private volatile boolean allowCoreThreadTimeOut;
    //核心线程数
    private volatile int corePoolSize;
    //线程池能创建的最大线程数
    private volatile int maximumPoolSize;
    //默认的任务拒绝策略是抛出异常
    private static final RejectedExecutionHandler defaultHandler = new ThreadPoolExecutor.AbortPolicy();
    private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread");
    private static final boolean ONLY_ONE = true;
public void execute(Runnable command) {
        if (command == null) {
            throw new NullPointerException();
        } else {
            //存储了两部分信息,当前线程池的线程数,线程池的运行状态
            int c = this.ctl.get();
            //1.线程池的线程数小于核心线程数
            if (workerCountOf(c) < this.corePoolSize) {
             //创建新的核心线程执行任务,参数为true
                if (this.addWorker(command, true)) {
                    return;
                }
                c = this.ctl.get();
            }
            //如果线程池的线程数 >=核心线程数,把任务放到任务队列
            if (isRunning(c) && this.workQueue.offer(command)) {
                int recheck = this.ctl.get();
                if (!isRunning(recheck) && this.remove(command)) {
                    this.reject(command);
                } else if (workerCountOf(recheck) == 0) {
                    this.addWorker((Runnable)null, false);
                }
            //如果队列也满了,就创建非核心线程执行任务,false表示创建的线程为非核心线程,addWorker会判断当前线程数是否大于最大线程数,如果是,则会执行任务拒绝策略   
            } else if (!this.addWorker(command, false)) {
               //执行任务拒绝策略。
                this.reject(command);
            }

        }
    }
}

核心线程的特殊配置:

核心线程默认不会预先创建,只有有任务时才创建。也不会空闲而被终止,keepAliveTime也不使用它。不过ThreadPoolExecutor也提供了方法,可以改变这个默认行为:

方法名返回值说明
prestartAllCoreThreadsint预先创建所有的 核心线程
prestartCoreThreadboolean创建一个核心线程,如果所有核心线程已创建,则返回false
allowCoreThreadTimeoOut (boolean value)void如果参数是 true,则keepAliveTime 也对核心线程适用

工厂类Executors:

//Executors 创建只有一个线程的线程池,keepAliveTime是0表示线程永远不会被终止,使用的是无界单链表队列LinkedBlockingQueue。线程池中的线程执行所有任务,适用于需要确保所有任务被顺利执行的场合。但由于是无界队列,排队任务过多,可能会消耗过多的内存。
public static ExecutorService newSingleThreadExecutor() {
        return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
    }
//核心线程数是固定的n,最大线程数也是n,存活时间是0,且使用的队列是无界的LinkedBlockingQueue。这个线程池固定的使用n个线程,线程不会被终止。由于是无界队列,排队过多,会消耗过多的内存。 
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
    }
//核心线程是0表示线程池内的所有线程都受存活时间控制,线程最大值是Integer.MAX_VALUE,且每个线程的空闲存活时间是60秒,使用的是没有存储空间的SynchronousBlockingQueue阻塞队列。这个线程池有新任务来的时候,除非有空闲线程,否则总是创建新线程,创建的线程不受限制。        
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), threadFactory);
    }

使用场景:

(1)在系统负载很高的时候:使用newFixedThreadPool()更使用。因为newFixedThreadPool()通过对新任务进行排队,保证有足够的资源处理这些实际的任务。而newCachedThreadPool是为每一个队列创建一个线程,导致创建过多的线程竞争cpu和内存资源,使得实际任务难以完成。

(2)在系统负责不是很高的时候,且单个任务执行时间也比较短,newCachedThreadPool的效率会更高。因为任务可以不用排队,直接交给空闲的线程。

(3)在系统负载极高的时候,上面两种都不适合,这是应该根据情况自定义ThreadPoolExecutor,传递合适的参数。

线程池的死锁:

当任务之间依赖,可能会出现死锁。比如任务A,在它执行的过程中,它给同样的任务执行服务提交了一个任务B,但需要等待B结束。如果任务A是提交给了一个单线程的线程池,一定会出现死锁,A在等待B的结果,而B在队列中等待被调度。如果是提交给一个限定线程个数的线程池,也有可能因线程数限制出现死锁。
解决方案:
(1)是用newCachedThreadPool()创建线程池,让线程数不受限制。
(2)使用synchronousQueue,它可以避免死锁。因为当任务入队成功就意味已有线程接受处理,如果入队失败,可以创建更多线程直到到达线程池的最大个数。如果到达最大个数会触发拒绝机制,不管怎样都会发生死锁。

总结:

线程池实现了生产者消费者模式,工作者线程是消费者,任务提交者是生产者。当遇到类似生产者和消费者问题时,直接使用线程池。线程池的参数很重要,理解参数的含义可以更好的帮助我们使用好线程池。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值