Java 线程池

什么是线程池?为什么要使用线程池?

线程池是一种多线程的处理方式,将任务提交给线程池中的线程去处理,任务的处理由线程池管理。如果在开发过程中给每个任务都去单独的创建一个独立的线程,那么系统的资源很快就会被耗尽。创建线程和销毁线程的系统开销较大,需要的时间开销可能比业务处理的时间还要长,所以要减少创建线程和销毁线程的次数,利用线程池中的线程来分别执行不同的任务。

线程池有什么作用?

  1. 提高效率,线程池中的核心线程会一直处于存活状态,当需要使用时,直接去线程池中取就可以,不需要创建线程,系统效率会得到提高。
  2. 可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。

ThreadPoolExecutor的构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
ThreadPoolExecutor构造方法参数说明

corePoolSize

指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去;
maximumPoolSize指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;
keepAliveTime当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;
unitkeepAliveTime的单位
workQueue任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
threadFactory线程工厂,用于创建线程,一般用默认即可;
handler

拒绝策略;当任务太多来不及处理时,如何拒绝任务;

 

 

 

 

 

 

 

 

 

 

任务队列详解:

  1. SynchronousQueue

SynchronousQueue是一个没有容量的队列,它不会把任务缓存起来,提交的任务会立即执行。所以,提交的任务数大于最大线程数时,就会执行拒绝策略。

   SynchronousQueue案例:

package com.wyx.learn.concurrent.ThreadPool;

import java.util.concurrent.*;

/**
 * @program: concurrent
 * @description: 直接提交队列
 * @author: Wang Yongxin
 * @create: 2019-11-19 10:47
 **/
public class SynchronousQueueTest {
    private static ExecutorService executorService;
    public static void main(String[] args) {
        executorService = new ThreadPoolExecutor(
                1,                                       //核心线程数
                3,                                       //最大线程数
                2,                                       //生存时间
                TimeUnit.SECONDS,                        //生存时间单位
                new SynchronousQueue<Runnable>(),        //任务队列
                Executors.defaultThreadFactory(),        //线程工厂
                new ThreadPoolExecutor.AbortPolicy()     //拒绝策略
        );
        for(int i = 0;i < 4;i++){
           executorService.submit(new Thread(()->{
               System.out.println(Thread.currentThread().getName());
           }));
        }
        executorService.shutdown();
    }
}

  运行结果:

pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@27bc2616 rejected from java.util.concurrent.ThreadPoolExecutor@3941a79c[Running, pool size = 3, active threads = 0, queued tasks = 0, completed tasks = 3]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at com.wyx.learn.concurrent.ThreadPool.SynchronousQueueTest.main(SynchronousQueueTest.java:17)

只能执行前三个任务,第四个任务提交时,已经超出了最大线程数,所以触发拒绝策略,抛出了异常。

2、ArrayBlockingQueue

ArrayBlockingQueue是有界任务队列,当线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。

ArrayBlockingQueue案例:

package com.wyx.learn.concurrent.ThreadPool;

import java.util.concurrent.*;

/**
 * @program: concurrent
 * @description: 有界任务队列
 * @author: Wang Yongxin
 * @create: 2019-11-19 11:10
 **/
public class ArrayBlockingQueueTest {
    private static ExecutorService executorService;
    public static void main(String[] args) {
        executorService = new ThreadPoolExecutor(
                2,                                       //核心线程数
                3,                                       //最大线程数 
                2,                                       //生存时间
                TimeUnit.SECONDS,                        //生存时间单位
                new ArrayBlockingQueue<>(2),             //任务队列
                Executors.defaultThreadFactory(),        //线程工厂
                new ThreadPoolExecutor.AbortPolicy()     //拒绝策略
        );
        for(int i = 0;i <6;i++){
            executorService.submit(new Thread(()->{
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }));
        }
        executorService.shutdown();
    }

}

运行结果:

当创建的任务数大于5时,会触发拒绝策略。

pool-1-thread-3
pool-1-thread-2
Exception in thread "main" pool-1-thread-1
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@7530d0a rejected from java.util.concurrent.ThreadPoolExecutor@27bc2616[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at com.wyx.learn.concurrent.ThreadPool.ArrayBlockingQueueTest.main(ArrayBlockingQueueTest.java:24)
pool-1-thread-2
pool-1-thread-3

3、LinkedBlockingQueue

使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。

4、PriorityBlockingQueue

除了第一批任务直接创建线程执行外,其他的任务都被放入了优先任务队列,按优先级进行了重新排列执行,且线程池的线程数一直为corePoolSize,通过运行的代码我们可以看出PriorityBlockingQueue它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

PriorityBlockingQueue案例:

package com.wyx.learn.concurrent.ThreadPool;

import java.util.concurrent.*;

/**
 * @program: concurrent
 * @description: 优先队列
 * @author: Wang Yongxin
 * @create: 2019-11-19 11:32
 **/
public class PriorityBlockingQueueTest {
    private static ExecutorService pool;
    public static void main( String[] args )
    {
        //优先任务队列
        pool = new ThreadPoolExecutor(
                3,
                4,
                1000,
                TimeUnit.MILLISECONDS,
                new PriorityBlockingQueue<Runnable>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        for(int i=0;i<20;i++) {
            pool.execute(new ThreadTask(i));
        }
    }
}

class ThreadTask implements Runnable,Comparable<ThreadTask>{

    private int priority;

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public ThreadTask() {

    }

    public ThreadTask(int priority) {
        this.priority = priority;
    }

    //当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高
    public int compareTo(ThreadTask o) {
        return  this.priority > o.priority ? -1 : 1 ;
    }

    public void run() {
        try {
            //让线程阻塞,使后续任务进入缓存队列
            Thread.sleep(1000);
            System.out.println("priority:"+this.priority+",ThreadName:"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

运行结果:

priority:0,ThreadName:pool-1-thread-1
priority:2,ThreadName:pool-1-thread-3
priority:1,ThreadName:pool-1-thread-2
priority:18,ThreadName:pool-1-thread-3
priority:19,ThreadName:pool-1-thread-1
priority:17,ThreadName:pool-1-thread-2
priority:15,ThreadName:pool-1-thread-1
priority:16,ThreadName:pool-1-thread-3
priority:14,ThreadName:pool-1-thread-2
priority:12,ThreadName:pool-1-thread-1
priority:13,ThreadName:pool-1-thread-3
priority:11,ThreadName:pool-1-thread-2
priority:10,ThreadName:pool-1-thread-1
priority:8,ThreadName:pool-1-thread-2
priority:9,ThreadName:pool-1-thread-3
priority:7,ThreadName:pool-1-thread-1
priority:6,ThreadName:pool-1-thread-2
priority:5,ThreadName:pool-1-thread-3
priority:4,ThreadName:pool-1-thread-1
priority:3,ThreadName:pool-1-thread-2

拒绝策略

为防止系统资源被耗尽,在创建线程池时,任务队列都会选择创建有界任务队列,当创建的线程数达到设置的最大线程数时,这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池"超载"的情况。ThreadPoolExecutor自带的拒绝策略如下:

AbortPolicy该策略会直接抛出异常,阻止系统正常工作;
CallerRunsPolicy把任务队列中的任务放在调用者线程当中运行;
DiscardOledestPolicy该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;
DiscardPolicy该策略会默认丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值