频道栏目
首页 > 资讯 > 其他 > 正文

Java并发编程系列(七)线程池的使用

17-02-13        来源:[db:作者]  
收藏   我要投稿

Java并发编程系列(七)线程池的使用:如果用new Thread().start的方式使用线程,当线程执行完毕后会销毁,如果频繁地创建和销毁线程,对系统的消耗将会非常大。

那么如果线程执行完任务后还可以重新使用,那么就可以提高性能。线程JDK里面线程池主要使用的是ThreadPoolExecutor,现在来初步感受一下线程池的使用:

package com.rancho945.concurrent;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class Test {

    public static void main(String[] args) {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 100, TimeUnit.SECONDS, new LinkedBlockingQueue());

        for (int i = 0; i < 8; i++) {
            //只要实现了Runnable接口的类都可以往里面扔
            executor.execute(new WorkTask(i));
        }
    }

    static class WorkTask implements Runnable{
        final private int taskNum;

        public WorkTask(int num) {
            taskNum  = num;
        }

        @Override
        public void run() {
            System.out.println("hello world,I am task "+taskNum+"----executed by "+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

}

执行结果:

hello world,I am task 1----executed by pool-1-thread-2
hello world,I am task 2----executed by pool-1-thread-3
hello world,I am task 0----executed by pool-1-thread-1
hello world,I am task 3----executed by pool-1-thread-4
hello world,I am task 4----executed by pool-1-thread-5
hello world,I am task 5----executed by pool-1-thread-2
hello world,I am task 6----executed by pool-1-thread-4
hello world,I am task 7----executed by pool-1-thread-1

使用线程池的主要步骤是,先创建线程池,然后把实现了Runnable接口的对象传进execute方法中即可。下面我们来仔细说说有关线程池的那些事。

线程池的创建

通过构造函数创建

ThreadPoolExecutor的构造函数有四个:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

这些构造函数最终都会调用到最后一个构造函数:

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

我们一个一个来解释一些这些构造函数的含义:

corePoolSize

核心池的大小。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。如果在执行任务之前调用了prestartAllCoreThreads()会创建好所有的核心线程,调用prestartCoreThread()方法会创建好一个核心线程。

maximumPoolSize

最大线程数量,包含corePoolSize。当任务超过核心线程池大小的时候,先把任务放在同步队列中,如果同步队列满了,再创建线程,直到线程池大小到达maximumPoolSize为止。

keepAliveTime

线程存活时间,默认只针对非核心线程。如果一个非核心线程在空闲状态下,到达了keepAliveTime,那么该线程会销毁。如果调用了allowCoreThreadTimeOut(true),那么对核心线程也会起作用,直到线程数为零。

unit

keepAliveTime的时间单位,有以下几种:

1. TimeUnit.DAYS;           //天
2. TimeUnit.HOURS;          //小时
3. TimeUnit.MINUTES;        //分钟
4. TimeUnit.SECONDS;        //秒
5. TimeUnit.MILLISECONDS;   //毫秒
6. TimeUnit.MICROSECONDS;   //微妙
7. TimeUnit.NANOSECONDS;    //纳秒

workQueue

实现了BlockingQueue的阻塞队列,用来存储等待执行的任务,超出核心线程池的任务会放在这个队列当中。阻塞队列主要有以下几种选择:

//基于数组的阻塞队列,创建的时候必须指定大小
1. ArrayBlockingQueue;

//基于链表的阻塞队列,可以不指定大小,默认为Integer.MAX_VALUE
2. LinkedBlockingQueue; 

//同步队列,它是没有容量的,不会保存提交的任务。
//也就是说如果使用这个队列,那么当任务数超出核心线程数后,直接新建非核心线程执行新任务。
3. SynchronousQueue;

//有优先级的阻塞队列,如果任务有优先级,那么会根据优先级来取出任务,比如后面加进来的任务优先级比较高,那么会执行后面的任务,而不是哪个先进队就执行哪个。
4. PriorityBlockingQueue 

threadFactory

线程工厂,主要是创建线程。默认为Executors中的DefaultThreadFactory,这里暂时不用管,只知道能创建线程即可。也可以自己定义,只需要实现ThreadFactory接口即可。

handler

当任务超出最大线程值+队列最大值的时候,线程池就会把提交的任务拒绝。handler作为拒绝任务的处理策略,有下面几种

//丢弃任务并抛出RejectedExecutionException异常,线程池默认是该策略
1. ThreadPoolExecutor.AbortPolicy。

//什么都不做,默默地丢弃任务,也不抛出异常。
2. ThreadPoolExecutor.DiscardPolicy 

//丢弃队列最前面的任务,然后重新尝试执行任务(如果还是失败则重复此过程)
3. ThreadPoolExecutor.DiscardOldestPolicy

//由调用线程处理该任务,比如说在主线程中调用线程池的execute方法被拒绝,该任务在主线程中执行,相当于在主线程中直接执行run方法。
4. ThreadPoolExecutor.CallerRunsPolicy 

通过静态方法创建线程。

Executors(注意多了个s)类提供了几个静态方法创建线程池,一般情况下用静态方法创建线程更加方便一些:

//创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newCachedThreadPool();  
//创建容量为1的缓冲池(单线程线程池)
Executors.newSingleThreadExecutor(); 
//创建固定容量大小的缓冲池
Executors.newFixedThreadPool(int);   

如果理解了前面的,那么看看这几个静态方法便一目了然。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue()));
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue());
}

线程池的关闭

线程池的关闭有两个方法:

//调用该方法后不会接受新的任务,等待队列中的任务执行完成后关闭线程池
public void shutdown();

//该方法尝试中断正在执行的任务,中断的方式是调用线程的interrupt方法,并且只调用一次,如果线程再次阻塞,则不能保证任务能够完全执行完。队列中没有执行完的任务以list的形式返回。
public List shutdownNow();

另外线程池还有submit、invokeAll、invokeAny用于执行有返回结果以及批量执行有返回结果的任务。这里涉及到Future等接口,这里暂时不说。

相关TAG标签
上一篇:Java开发之对象的行为
下一篇:配置SSH的时候,整合struts2和spring的时候,bean一直报defind
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站