线程池的简单使用_md

线程池

1. 为什么使用线程池

现象

  • 一个任务的到来,会伴随着线程的创建,当处理完任务后,线程会被销毁,资源回收

缺点 :

  1. 为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多

  2. 活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”,为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。

线程池的好处:

(实现)通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。

  1. 因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。
  2. 通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。

2. 常用的几种线程池(JAVA封装的)

2.1 newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

特点:

  • 线程创建数量没有限制 (0x7fffffff)

  • 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

特点: 最大为 Integer.MAX_VALUE , 可以释放线程

注意 : 在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0 ; i < j; i++) {
        final int index = i;
        executorService.execute(() -> {
            System.out.println(index);
            Thread.currentThread().setName("线程 " + index);
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

2.2 newFixedThreadPool

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

特点: 定长, 一但被创建就不会被释放, 抛异常没有处理会关闭这个线程创建新的线程
    ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0 ; i < j ; i++) {
            final int index = i;
            executorService.execute(() -> {
                Thread.currentThread().setName("线程 " + index);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

2.3 newSingleThreadExecutor

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

特点:单线程,如果这个线程异常结束,会有另一个取代它
static void testSingleThreadPool() {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0 ; i < 10 ; i++) {
            final int index = i;
            executorService.execute(() -> {
                //System.out.println(Thread.currentThread().getName() + "   " + 3 / (index %2));
                System.out.println(Thread.currentThread().getName() + "  " + index);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

2.4 newScheduleThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

扫描二维码关注公众号,回复: 4594774 查看本文章
特点:定长, 可以延时或者循环执行, 一旦从报错就停止运行, 但线程不释放
 /**
     * scheduledThreadPool
     * 延时
     * */
    static void testscheduledThreadPool() {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "  " +"delay 3 seconds");
                System.out.println(Thread.currentThread().getName() + "  " +new Date());
            }
        }, 3, TimeUnit.SECONDS);
        System.out.println(new Date());
    }

    /**
     * 延时, 循环执行
     * */
    static void testscheduledCircle(int j) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < j; i++) {
            scheduledExecutorService.scheduleAtFixedRate(new scheduledRunnable(i), 1, 3, TimeUnit.SECONDS);
            System.out.println(new Date());
        }
    }



    class scheduledRunnable implements Runnable{
        public int index;

        scheduledRunnable(int index) {
            this.index = index;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "    " + index + "    " + "delay 3 seconds");
        }
    }

3. 多线程使用

ThreadPoolExecutor CRAWL_POOL = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

3.1 使用方法

    ThreadPoolExecutor CRAWL_POOL = new ThreadPoolExecutor(10, 12, 8, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

    CRAWL_POOL.submit(new Callable<String>() {
        @Override
        public String call(){
            return "haha";
        }
    });

    CRAWL_POOL.execute(new Runnable() {
        @Override
        public void run(){
            return "haha";
        }
    });

3.2 构造方法

java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类

***   public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);

CachedThreadPool

new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());

FixedThreadPool

new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());

SingleThreadPool

new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())

ScheduledThreadPool

Timer + ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)

3.3 参数

  • corePoolSize

    核心线程数大小, 一般刚创建的线程池没有任何线程, 只有有任务的时候才会开始创建线程, 超过核心线程数 会放在任务队列里边

  • maximumPooSize

    最大线程数, 表示线程池最多创建多少个线程, 只有任务队列已经满之后才会创建新的线程

  • keepAliveTime

    线程存活时间, 这个参数默认只对超过核心线程数的线程生效, 对核心线程不生效, 但是调用 allowCoreThreadTimeOut(boolean) 时, 会对所有线程生效

  • unit

    线程存活时间的单位

  • BlockingQueue

    一个阻塞队列, 用于存储执行等待的任务, 一般会用三种队列

    LinkedBlockingQueue; (常用)(一个由链表结构组成的有界阻塞队列。 大小默认值为Integer.MAX_VALUE)
    ArrayBlockingQueue; (一个由数组结构组成的有界阻塞队列。其内部按先进先出的原则对元素进行排序,)

    在正常情况下,链接队列的吞吐量要高于基于数组的队列(ArrayBlockingQueue),因为其内部实现添加和删除操作使用的两个ReenterLock来控制并发执行,而ArrayBlockingQueue内部只是使用一个ReenterLock控制并发,因此LinkedBlockingQueue的吞吐量要高于ArrayBlockingQueue

    SynchronousQueue;(一个不存储元素的阻塞队列。)

  • ThreadFactory threadFactory

    线程工厂,主要用来创建线程;

          public interface ThreadFactory {
              /**
               * Constructs a new {@code Thread}.  Implementations may also initialize
               * priority, name, daemon status, {@code ThreadGroup}, etc.
               *
               * @param r a runnable to be executed by new thread instance
               * @return constructed thread, or {@code null} if the request to
               *         create a thread is rejected
               */
              Thread newThread(Runnable r);
          }
  • RejectedExecutionHandler handler

    表示当拒绝处理任务时的策略,有以下四种取值:

    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常, 中断程序运行。
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务,并且重复执行execute方法,知道成功为止

3.4 线程池中添加任务调用规则

1.线程池中线程的数量小于 corePoolSize(不论是活动还是休眠状态), 会创建新的线程.
2.线程池中线程的数量等于 corePoolSize, 当存在休眠线程的时候会优先使用休眠线程.
3.线程池中线程的数量等于 corePoolSize, 但是核心线程不存在休眠线程时会将任务假如阻塞队列, 等待线程空闲之后执行任务.
4.线程池中线程的数量等于 corePoolSize, 并且阻塞队列满的时候就会创建新的线程, 最大线程数为 maximumPooSize.
5.线程池中线程的数量大于 最大线程数的时候, hi通过 handler 的规则进行处理任务

3.4 常用方法

execute()

提交任务,通过这个方法可以向线程池提交一个任务,交由线程池去执行

submit()

(父类 AbstractExecutorService) 提交任务, 不过可以返回任务的结果

shutdown()

不再接受新的线程,并且等待之前提交的线程都执行完在关闭,

shutdownNow()

shutDownNow 直接关闭活跃状态的所有的线程 , 并返回等待中的线程 List

getQueue()

阻塞队列

getPoolSize()

(同步) 当前线程池内的线程数

getActiveCount()

当前线程池内活跃的线程数

getCompletedTaskCount()

返回执行完成的任务总数,只是一个近似值, 只增不减

remove

移除阻塞队列里的任务

猜你喜欢

转载自www.cnblogs.com/lidejie34/p/10157376.html