六、线程池

首先,为什么要使用线程池?当我们写helloworld的时,简简单单创建一个线程处理简简单单的任务,那肯定不需要线程池。但是对于需要处理大量请求诸如web服务器这样的应用来说,如果每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。那么,服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。

在java.util.concurrent.Executors类中提供四种线程池:

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若缓存里没有可用的线程,则新建线程。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

示例一:

public class ThreadPoolTest {

    public static void main(String[] args) {
        //固定的线程池
        ExecutorService executor= Executors.newFixedThreadPool(2);
        /**
         * 这里有五个Runnable任务,但是threadPool只有2个线程,所以每次只能执行两个任务。
         */
        for(int i = 1;i<=5;i++){
            final int temp = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    for(int j=1;j<=10;j++){
                        System.out.println(Thread.currentThread()+":i="+temp+"---j="+j);
                    }
                }
            });
        }
        threadPool.shutdown();  //当任务完成,结束线程
//        threadPool.shutdownNow();//立即结束线程

    }
}

结果:

Thread[pool-1-thread-1,5,main]:i=1---j=1
Thread[pool-1-thread-1,5,main]:i=1---j=2
Thread[pool-1-thread-2,5,main]:i=2---j=1
.......
Thread[pool-1-thread-1,5,main]:i=1---j=9
Thread[pool-1-thread-1,5,main]:i=1---j=10
Thread[pool-1-thread-2,5,main]:i=2---j=4
Thread[pool-1-thread-1,5,main]:i=3---j=1
......
Thread[pool-1-thread-2,5,main]:i=2---j=8
Thread[pool-1-thread-1,5,main]:i=3---j=2
Thread[pool-1-thread-2,5,main]:i=2---j=9
Thread[pool-1-thread-2,5,main]:i=2---j=10
Thread[pool-1-thread-2,5,main]:i=4---j=1

......

上面的示例,executor循环地执行Runnable任务。但是无论循环几次,每次只有2两个线程处理其中的两个任务,只有一个任务处理完毕,才处理下一个任务,这就是newFixedThreadPool固定的线程池。

示例二:

public class ThreadPoolTest {

    public static void main(String[] args) {

        ExecutorService threadPool2 = Executors.newCachedThreadPool();  //当有Runnable任务,自动增加线程执行
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();  //单个线程,和Thread一样。线程池里面始终都有一个线程,而且,可能一直是同一个线程。而Thread创建的线程结束了就结束了
        /**
         * 这里有五个Runnable任务,但是threadPool只有2个线程,所以每次只能执行两个任务。
         * 而且,必须得线程池里面所有线程的任务都完成,才能执行其它任务。
         */
        for(int i = 1;i<=5;i++){
            final int temp = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    for(int j=1;j<=10;j++){
                        System.out.println(Thread.currentThread()+":i="+temp+"---j="+j);
                    }
                }
            });
        }
        threadPool.shutdown();  //当任务完成,结束线程
     }
}

打印结果:

Thread[pool-1-thread-1,5,main]:i=1---j=1
Thread[pool-1-thread-1,5,main]:i=1---j=2
Thread[pool-1-thread-2,5,main]:i=2---j=1
......
Thread[pool-1-thread-3,5,main]:i=2---j=10
Thread[pool-1-thread-4,5,main]:i=4---j=1
Thread[pool-1-thread-5,5,main]:i=5---j=1
......

与示例一不同是:newCachedThreadPool当线程不足时,会自动创建新的线程。

示例三:

public class ThreadPoolTest {

    public static void main(String[] args) {
        //定时器线程池
        ScheduledExecutorService threadPool2 = Executors.newScheduledThreadPool(2);
        threadPool2.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hehe!");
            }
        },2, TimeUnit.SECONDS);  //TimeUnit单位,这里设定为秒,所以延迟2秒后执行

        threadPool2.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("xixi");
            }
        }, 5, 2, TimeUnit.SECONDS);   //开始5秒之后执行一次,然后每隔2秒执行一次。
        //要安排在某个以后的 Date 运行,可以使用:schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS)

    }
}

关于 scheduleAtFixedRate()和scheduleWithFixedDealy()。

1、scheduleAtFixedRate 方法,顾名思义,它的方法名称的意思是:已固定的频率来执行某项计划(任务)。
2、scheduleWithFixedDealy,相对固定的延迟后,执行某项计划。

还是比较简单明了的描述比较好:第一个方法是固定的频率来执行某项计划,它不受计划执行时间的影响。到时间,它就执行。

而第二个方法,相对固定,据鄙人理解,是相对任务的。即无论某个任务执行多长时间,等执行完了,我再延迟指定的时间。也就是第二个方法,它受计划执行时间的影响。

时间: 2024-10-10 00:24:48

六、线程池的相关文章

Java并发基础(六) - 线程池

Java并发基础(六) - 线程池 1. 概述 这里讲一下Java并发编程的线程池的原理及其实现 2. 线程池的基本用法 2.1 线程池的处理流程图 该图来自<Java并发编程的艺术>: 从图中我们可以看出当一个新任务到线程池时,线程池的处理流程如下: 线程池首先判断线程池里面线程数是否达到核心线程数.如果不是则直接创建新线程作为核心线程来执行该任务(该线程作为核心线程不会由于任务的完成而销毁),否则进入下一流程. 判断阻塞队列是否已经满了.如果没满则将该任务放入阻塞队列中,等待核心线程处理,

JAVA多线程提高六:java5线程并发库的应用_线程池

前面我们对并发有了一定的认识,并且知道如何创建线程,创建线程主要依靠的是Thread 的类来完成的,那么有什么缺陷呢?如何解决? 一.对比new Threadnew Thread的弊端 a. 每次new Thread新建对象性能差. b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom. c. 缺乏更多功能,如定时执行.定期执行.线程中断.相比new Thread,Java提供的四种线程池的好处在于:a. 重用存在的线程,减少对象创建.消亡的开销,性能

多线程篇六:线程池

1.固定大小的线程池 ExecutorService threadPools1=Executors.newFixedThreadPool(3); for(int i=1;i<=10;i++){ final int task=i; //循环10次,一共往线程池里面放10个任务 threadPools1.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().ge

小白成长之路:初识python(六) --python线程池

#!/usr/bin/env python# -*- coding:utf-8 -*-import threadingimport queueimport time"""对照着武老师的课程自己跟着做了一个线程池,主要的思路就是把要执行的任务放进队列中然后创建若干个线程不断地从队列中获取任务并执行相对比low B 版的线程池有很大改进,姑且叫low A版吧...""" Stop_Flag = object() class ThreadPool(ob

Nginx 学习笔记(六)引入线程池 性能提升9倍

原文地址:https://www.cnblogs.com/shitoufengkuang/p/4910333.html 一.前言 1.Nignx版本:1.7.11 以上 2.NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求. 3.NGINX工作在非阻塞的socket模式下,并使用了epoll 和 kqueue这样有效的方法. 4.NGINX可以非常好地处理百万级规模的并发请求

多线程系列六:线程池

一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任务:T1创建线程时间,T2任务执行时间,T3线程销毁时间,线程池空闲的时候可以去执行T1和T2,从而提高响应 c) 提高线程的可管理性. 使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死

Java多线程系列 JUC线程池07 线程池原理解析(六)

 关闭“线程池” shutdown()的源码如下: public void shutdown() { final ReentrantLock mainLock = this.mainLock; // 获取锁 mainLock.lock(); try { // 检查终止线程池的“线程”是否有权限. checkShutdownAccess(); // 设置线程池的状态为关闭状态. advanceRunState(SHUTDOWN); // 中断线程池中空闲的线程. interruptIdleWork

.NET线程池技术实现多任务批量处理

一.多线程技术应用场景介绍 本期同样带给大家分享的是阿笨在实际工作中遇到的真实业务场景,请跟随阿笨的视角去如何采用基于开源组件SmartThreadPool线程池技术实现多任务批量处理.在工作中您是否遇到过如何快速高效的处理Job任务列表.如何通过多线程批量处理订单.如何多线程群发短信.如何批量上传图片到远程图片服务器或者云存储图片服务器.如何通过多线程让应用程序提高对CPU的利用率从而增加应用程序的处理效率,等等.如果您有遇到类似的业务场景的而感到烦恼的话,那么今天您看完阿笨的分享课后下次碰到

C++11线程池的实现

什么是线程池 处理大量并发任务,一个请求一个线程来处理请求任务,大量的线程创建和销毁将过多的消耗系统资源,还增加了线程上下文切换开销. 线程池通过在系统中预先创建一定数量的线程,当任务请求到来时从线程池中分配一个预先创建的线程去处理任务,线程在处理任务之后还可以重用,不用销毁,从而节省系统资源.对于多核处理器,线程会被分配到多个CPU,提高并行处理效率.每个线程独立阻塞,防止主线程被阻塞而使主流程被阻塞 半同步半异步线程池 三层 第一层:同步服务层,处理上层任务请求 第二层:同步排队层,上层的任