线程池

参考地址:https://blog.csdn.net/djzhao/article/details/82192918#commentBox
如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁线程,如此一来会大大降低系统的效率。可能出现服务器在为每个请求创建新线程和销毁线程上花费的时间和消耗的系统资源要比处理实际的用户请求的时间和资源更多。

什么时候使用线程池?
单个任务处理时间比较短
需要处理的任务数量很大

使用线程池的好处
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

只做简单的使用例子
ExecutorService executorService = new ThreadPoolExecutor(10, 50,60L, TimeUnit.SECONDS,new SynchronousQueue(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

ThreadPoolExecutor构造器分析
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue 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;
}
构造方法中的字段含义如下:

corePoolSize:核心线程数量,当有新任务在execute()方法提交时,会执行以下判断:
如果运行的线程少于 corePoolSize,则创建新线程来处理任务,即使线程池中的其他线程是空闲的;
如果线程池中的线程数量大于等于 corePoolSize 且小于 maximumPoolSize,则只有当workQueue满时才创建新的线程去处理任务;
如果设置的corePoolSize 和 maximumPoolSize相同,则创建的线程池的大小是固定的,这时如果有新任务提交,若workQueue未满,则将请求放入workQueue中,等待有空闲的线程去从workQueue中取任务并处理;
如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务;
所以,任务提交时,判断的顺序为 corePoolSize –> workQueue –> maximumPoolSize。
maximumPoolSize:最大线程数量;
workQueue:等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;
workQueue:保存等待执行的任务的阻塞队列,当提交一个新的任务到线程池以后, 线程池会根据当前线程池中正在运行着的线程的数量来决定对该任务的处理方式,主要有以下几种处理方式:
直接切换:这种方式常用的队列是SynchronousQueue,但现在还没有研究过该队列,这里暂时还没法介绍;
使用无界队列:一般使用基于链表的阻塞队列LinkedBlockingQueue。如果使用这种方式,那么线程池中能够创建的最大线程数就是corePoolSize,而maximumPoolSize就不会起作用了(后面也会说到)。当线程池中所有的核心线程都是RUNNING状态时,这时一个新的任务提交就会放入等待队列中。
使用有界队列:一般使用ArrayBlockingQueue。使用该方式可以将线程池的最大线程数量限制为maximumPoolSize,这样能够降低资源的消耗,但同时这种方式也使得线程池对线程的调度变得更困难,因为线程池和队列的容量都是有限的值,所以要想使线程池处理任务的吞吐率达到一个相对合理的范围,又想使线程调度相对简单,并且还要尽可能的降低线程池对资源的消耗,就需要合理的设置这两个数量。
如果要想降低系统资源的消耗(包括CPU的使用率,操作系统资源的消耗,上下文环境切换的开销等), 可以设置较大的队列容量和较小的线程池容量, 但这样也会降低线程处理任务的吞吐量。
如果提交的任务经常发生阻塞,那么可以考虑通过调用 setMaximumPoolSize() 方法来重新设定线程池的容量。
如果队列的容量设置的较小,通常需要将线程池的容量设置大一点,这样CPU的使用率会相对的高一些。但如果线程池的容量设置的过大,则在提交的任务数量太多的情况下,并发量会增加,那么线程之间的调度就是一个要考虑的问题,因为这样反而有可能降低处理任务的吞吐量。
keepAliveTime:线程池维护线程所允许的空闲时间。当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;
threadFactory:它是ThreadFactory类型的变量,用来创建新线程。默认使用Executors.defaultThreadFactory() 来创建线程。使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。
handler:它是RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。线程池提供了4种策略:
AbortPolicy:直接抛出异常,这是默认策略;
CallerRunsPolicy:用调用者所在的线程来执行任务;
DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
DiscardPolicy:直接丢弃任务;

使用CallerRunsPolicy的测试案例
Test

package month4.线程池;


import java.util.concurrent.*;

public class Test {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(3, 10,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 20; i++) {
            int j = i;
            executorService.execute(() -> {
                int k = j;
                try {
                    Thread.sleep(100L);
                } catch (Exception e) {
                }
                System.out.println(System.currentTimeMillis() + "---" + Thread.currentThread() + "hahah" + k);
            });
            executorService.execute(() -> {
                int k = j;
                try {
                    Thread.sleep(100L);
                } catch (Exception e) {
                }
                System.out.println(System.currentTimeMillis() + "---" + Thread.currentThread() + "hahaj" + k);
            });
            try {
                Thread.sleep(5L);
            } catch (Exception e) {
            }
        }
        System.out.println("end");
        executorService.shutdown();
    }
}

结果

1556097763903---Thread[pool-1-thread-2,5,main]hahaj0
1556097763903---Thread[pool-1-thread-1,5,main]hahah0
1556097763908---Thread[pool-1-thread-3,5,main]hahah1
1556097763908---Thread[pool-1-thread-4,5,main]hahaj1
1556097763913---Thread[pool-1-thread-5,5,main]hahah2
1556097763913---Thread[pool-1-thread-6,5,main]hahaj2
1556097763918---Thread[pool-1-thread-8,5,main]hahaj3
1556097763918---Thread[pool-1-thread-7,5,main]hahah3
1556097763923---Thread[pool-1-thread-9,5,main]hahah4
1556097763923---Thread[pool-1-thread-10,5,main]hahaj4
1556097763928---Thread[main,5,main]hahah5
1556097764028---Thread[pool-1-thread-10,5,main]hahaj5
1556097764033---Thread[pool-1-thread-7,5,main]hahaj6
1556097764033---Thread[pool-1-thread-9,5,main]hahah6
1556097764038---Thread[pool-1-thread-8,5,main]hahah7
1556097764038---Thread[pool-1-thread-6,5,main]hahaj7
1556097764043---Thread[pool-1-thread-5,5,main]hahah8
1556097764043---Thread[pool-1-thread-4,5,main]hahaj8
1556097764048---Thread[pool-1-thread-2,5,main]hahaj9
1556097764048---Thread[pool-1-thread-3,5,main]hahah9
1556097764053---Thread[pool-1-thread-1,5,main]hahah10
1556097764053---Thread[main,5,main]hahaj10
1556097764158---Thread[pool-1-thread-3,5,main]hahaj11
1556097764158---Thread[pool-1-thread-1,5,main]hahah11
1556097764163---Thread[pool-1-thread-2,5,main]hahah12
1556097764163---Thread[pool-1-thread-4,5,main]hahaj12
1556097764168---Thread[pool-1-thread-5,5,main]hahah13
1556097764168---Thread[pool-1-thread-6,5,main]hahaj13
1556097764173---Thread[pool-1-thread-8,5,main]hahah14
1556097764173---Thread[pool-1-thread-9,5,main]hahaj14
1556097764178---Thread[pool-1-thread-10,5,main]hahaj15
1556097764178---Thread[pool-1-thread-7,5,main]hahah15
1556097764183---Thread[main,5,main]hahah16
end
1556097764283---Thread[pool-1-thread-7,5,main]hahaj16
1556097764288---Thread[pool-1-thread-10,5,main]hahah17
1556097764288---Thread[pool-1-thread-9,5,main]hahaj17
1556097764293---Thread[pool-1-thread-8,5,main]hahah18
1556097764294---Thread[pool-1-thread-6,5,main]hahaj18
1556097764298---Thread[pool-1-thread-4,5,main]hahaj19
1556097764298---Thread[pool-1-thread-5,5,main]hahah19

Process finished with exit code 0

很明显,当拥堵而使用主程序时,for循环都暂停了

submit
之前测试使用execute方法,但是execute传参是(runnable)是不会有返回的,如果有返回,则使用submit方法
submit传参是Callable,Callable的call方法是有返回的
使用Future接收后,使用get获取
稍微了解了下Callable,Callable是个接口,和runnable类似,Runnable执行时需要Thread包装,callable需要FutureTask包装
FutureTask继承Runnable,充当Runnable使用
注意:Future接收到返回数据后,注意get的使用,因为get会阻塞当前线程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值