Java线程池

spring 框架下有封装好的线程池,尽量优先选择 spring 提供的。

不推荐新手学习,因为学习门槛非常高,需要有一定的封装经验,否则会很难理解它的设计;
而且实战的机会非常少,学完可能几年内都用不上。

不必担心面试提问,因为很少会直接使用,面试你的人大概率也是没用过的,用过也只能说明他可能在工作在摸鱼。

Executors

Executors提供四种线程池,分别为:

(注意名字带s,这是个工具类,不推荐使用Executors,应当显式创建线程池)

  1. newCachedThreadPool
    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程。如果任务执行结束,线程会被其他任务继续使用,而不用每次都新建线程。
  2. newFixedThreadPool
    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。
  3. newScheduledThreadPool
    创建一个定长线程池,支持定时及周期性任务执行,他有几个特殊的方法:
    schedule()方法更注重保持间隔时间的稳定;
    scheduleAtFixedRate()方法更注重保持执行频率的稳定,以固定的频率来执行某项计划(任务);
    scheduleWithFixedDelay(),相对固定的延迟后,执行某项计划。
  1. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

ThreadPoolExecutor

Executor和ThreadPoolExecutor的关系

1、Executor是一个顶层接口,在它里面只声明了一个方法 execute(Runnable),就是用来执行任务的;
2、ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
3、抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
4、而ThreadPoolExecutor继承自AbstractExecutorService。

ThreadPoolExecutor构造方法参数

corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 线程池中超过corePoolSize数目时,空闲线程的最大存活时间
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由execute方法提交的 Runnable 任务。
threadFactory - 执行程序创建新线程时使用的工厂。
handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

ThreadPoolExecutor构造参数的作用

一句话概括:小于maximumPoolSize就能新建线程,大于corePoolSize就放入WorkQueue缓冲队列;
超过最大处理能力就交给Handler处理,超过最大存活时间线程池就会关闭(超出corePoolSize部分,也可自定义),线程池分类不同,具体情况略有不同:

1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中小于corePoolSize的线程空闲时间达到keepAliveTime也将关闭

unit可选的参数(构造方法 keepAliveTime 参数)

keepAliveTime参数的时间单位,有以下可选:

TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒

阻塞队列(构造方法 workQueue 参数)

要处理的任务太多了,线程池处理不过来,那就先把任务缓存起来。
workQueue指的是阻塞队列,用来存储等待执行的任务,一般来说,这里的阻塞队列有以下几种选择:

PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
ArrayBlockingQueue: 规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的.
LinkedBlockingQueue: 基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

拒绝策略(构造方法 handler 参数)

要处理的任务还是太多,该怎么怎么办?表示当拒绝处理任务时的策略,有以下四种取值:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

较常用的方法

线程执行

  1. execute() - 在将来某个时间执行给定任务。可以在新线程中或者在现有池线程中执行该任务。如果无法将任务提交执行,或者因为此执行程序已关闭,或者因为已达到其容量,则该任务由当前
    RejectedExecutionHandler 处理。
  2. submit() - 提交一个事务(参数可以是:runnable对象,回调接口,或者是与泛型相同的对象),它能够返回任务执行的结果,

线程关闭

  1. shutdown() - 按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务。如果已经关闭,则调用没有其他作用。
  2. shutdownNow() - 尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表。在从此方法返回的任务队列中排空(移除)这些任务。
    并不保证能够停止正在处理的活动执行任务,但是会尽力尝试。 此实现通过 Thread.interrupt()
    取消任务,所以无法响应中断的任何任务可能永远无法终止。

其它

  1. purge() - 尝试从工作队列移除所有已取消的Future任务。此方法可用作存储回收操作,它对功能没有任何影响。取消的任务不会再次执行,但是它们可能在工作队列中累积,直到worker线程主动将其移除。调用此方法将试图立即移除它们。但是,如果出现其他线程的干预,那么此方法移除任务将失败。
  2. getQueue() - 返回此执行程序使用的任务队列。对任务队列的访问主要用于调试和监控。此队列可能正处于活动使用状态中。获取任务队列不妨碍已加入队列的任务的执行。
  3. remove() - 从执行程序的内部队列中移除此任务(如果存在),从而如果尚未开始,则其不再运行。

几个供子类重写的方法

  1. beforeExecute() - 在执行给定线程中的给定 Runnable 之前调用的方法。此方法由将执行任务 r 的线程 t 调用,并且可用于重新初始化 ThreadLocals 或者执行日志记录。
    此实现不执行任何操作,但可在子类中定制。注:为了正确嵌套多个重写操作,此方法结束时,子类通常应该调用
    super.beforeExecute。
  2. afterExecute() - 基于完成执行给定 Runnable 所调用的方法。此方法由执行任务的线程调用。如果非 null,则 Throwable 是导致执行突然终止的未捕获 RuntimeException 或 Error。
    注:当操作显示地或者通过submit 之类的方法包含在任务内时(如FutureTask),这些任务对象捕获和维护计算异常,因此它们不会导致突然终止,内部异常不会 传递给此方法。
    此实现不执行任何操作,但可在子类中定制。
    注:为了正确嵌套多个重写操作,此方法开始时,子类通常应该调用
    super.afterExecute。
  3. terminated() - 当 Executor 已经终止时调用的方法。默认实现不执行任何操作。
    注:为了正确嵌套多个重写操作,子类通常应该在此方法中调用super.afterExecute。
  4. finalize() - protected void finalize()当不再引用此执行程序时,调用 shutdown。

ThreadPoolExecutor类中其他的一些比较重要成员变量

private final BlockingQueue[Runnable] workQueue;
//任务缓存队列,用来存放等待执行的任务
private final ReentrantLock mainLock = new ReentrantLock();
//线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁
private final HashSet workers = new HashSet);
//用来存放工作集
private volatile long keepAliveTime; //线程存货时间
private volatile boolean allowCoreThreadTimeOut;
//是否允许为核心线程设置存活时间
private volatile int corePoolSize;
//核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
private volatile int maximumPoolSize; //线程池最大能容忍的线程数
private volatile int poolSize; //线程池中当前的线程数
private volatile RejectedExecutionHandler handler; //任务拒绝策略
private volatile ThreadFactory threadFactory; //线程工厂,用来创建线程
private int largestPoolSize; //用来记录线程池中曾经出现过的最大线程数
private long completedTaskCount; //用来记录已经执行完毕的任务个数

一些参考经验:

  1. 用ThreadPoolExecutor自定义线程池,看线程是的用途,如果任务量不大,可以用无界队列,如果任务量非常大,要用有界队列,防止OOM1.
  2. 如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交。保证不抛弃一个任务
  3. 最大线程数一般设为2N+1最好,N是CPU核数
  4. 核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数
  5. 如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果

相关代码

一种个人比较喜欢的关闭线程的方式
class MyRunnable implements Runnable{
    private boolean stop;
    private Thread thread;

    public MyRunnable() {
        stop = false;
        thread = Thread.currentThread();
    }

    @Override
    public void run() {
        int count = 0;
        while (!stop) {
            try {
                System.out.println(count++);
                Thread.sleep(200);
            } catch (InterruptedException e) {
                this.thread.interrupt();
            }
        }
    }

    public void stopThread() {
        this.stop = !stop;
    }
}

public class SafelyStop {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myRunnable.stopThread();
    }
}
工具代码

在实际项目开发过程中,没有太多实用价值,但是教学的时候,很方便实用,能快速构造起各种队列。


class ThreadUtil {
    private static ThreadPoolExecutor threadPool;
    private static ExecutorService cachedThreadPool;
    private static ExecutorService fixedThreadPool;
    private static ScheduledExecutorService scheduledThreadPool;
    private static ExecutorService singleThreadExecutor;

    /**
     * 个人比较喜欢的一种单例模式写法,不知道有没有人这么写
     *
     * @return 线程池
     */
    public static ThreadPoolExecutor getThreadpool() {
        if (threadPool == null)
            createThreadPool();
        return threadPool;
    }

    public static ExecutorService getCachedThreadPool() {
        if (cachedThreadPool == null)
            crateCachedThreadPool();
        return cachedThreadPool;

    }

    public static ExecutorService getFixedThreadPool() {
        if (fixedThreadPool == null)
            crateFixedThreadPool();
        return fixedThreadPool;

    }

    public static ScheduledExecutorService getScheduledThreadPool() {
        if (scheduledThreadPool == null)
            crateScheduledThreadPool();
        return scheduledThreadPool;
    }

    public static ExecutorService getSingleThreadExecutor() {
        if (singleThreadExecutor == null)
            crateSingleThreadExecutor();
        return singleThreadExecutor;
    }

    /**
     * 单例线程
     */
    private static void crateSingleThreadExecutor() {
        if (singleThreadExecutor == null)
            singleThreadExecutor = Executors.newSingleThreadExecutor(new CustomThreadFactory());
    }

    /**
     * 创建一个定长线程池,支持定时及周期性任务执行。
     */
    private static void crateScheduledThreadPool() {
        if (scheduledThreadPool == null)
            scheduledThreadPool = Executors.newScheduledThreadPool(3, new CustomThreadFactory());
    }

    /**
     * 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
     */
    private static void crateFixedThreadPool() {
        if (fixedThreadPool == null)
            fixedThreadPool = Executors.newFixedThreadPool(4, new CustomThreadFactory());
    }

    /**
     * 带缓存的队列,如果有新的事务,自动使用之前的线程,线程会自动回收
     */
    private static void crateCachedThreadPool() {
        if (cachedThreadPool == null)
            cachedThreadPool = Executors.newCachedThreadPool(new CustomThreadFactory());
    }

    /**
     * 自定义线程池
     */
    private synchronized static void createThreadPool() {
        if (threadPool == null)
            threadPool = new ThreadPoolExecutor(
                    3, // 线程数量
                    5, // 线程池最大值
                    2, // 最大存活时间
                    TimeUnit.SECONDS, // 按照秒计算
                    new ArrayBlockingQueue<Runnable>(5), // 缓冲池(阻塞队列)
                    new CustomThreadFactory(), // 线程工厂
                    new CustomRejectedExecutionHandler()// 超出最大处理能力处理机制(拒绝策略)
            );
    }

    /**
     * 关闭所有的线程
     */
    public static void destory() {
        if (!threadPool.isShutdown()) {
            threadPool.shutdownNow();
        }
    }

    /**
     * 自定义线程工厂,这里给每一个线程取了一个名字
     *
     * @author ChenSS
     *
     */
    private static class CustomThreadFactory implements ThreadFactory {

        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            String threadName = ThreadFactory.class.getSimpleName() + count.addAndGet(1);
            thread.setName(threadName);
            return thread;
        }
    }

    /**
     * 自定义处理策略
     *
     * @author ChenSS
     *
     */
    private static class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
            try {
                // 超出最大线程池极限的时候的处理机制,这里也可以直接拒绝、或者说抛出异常,而这里是等待
                executor.getQueue().put(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试函数

线程池有用,但是实践机会很少,很容易忘记,下面这个代码,可以用于测试各种线程池的功能。

package com;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    /**
     * 容量大小固定的线程池:因为线程池大小限制为4,所以一次输出4个数字,并且会发现线程被复用了
     */
    public static void testFixedThreadPool() {
        for (int i = 0; i < 10; i++) {
            final int index = i;
            ThreadUtil.getFixedThreadPool().execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName() + ":" + index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    /**
     * 带延迟效果的线程池:线程延迟了3秒执行,容量大小,而且线程只新建了3个
     */
    public static void testScheduledThreadPool1() {
        for (int i = 0; i < 10; i++) {
            ThreadUtil.getScheduledThreadPool().schedule(new Runnable() {

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

    /**
     * 带延迟效果的线程池:线程延迟了1秒执行,之后每3秒执行一次,而且线程是3个中的随机一个,以固定的周期执行
     */
    public static void testScheduledThreadPool2() {
        ThreadUtil.getScheduledThreadPool().scheduleWithFixedDelay(new Runnable() {

            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1, 2, TimeUnit.SECONDS);
    }

    /**
     * 带延迟效果的线程池:线程延迟了1秒执行,之后每3秒执行一次,而且线程是3个中的随机一个,执行完任务,以固定的周期进行延迟
     */
    public static void testScheduledThreadPool3() {
        ThreadUtil.getScheduledThreadPool().scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1, 2, TimeUnit.SECONDS);
    }

    /**
     * 可自动回收线程的线程池:Java默认60秒自动回收,测试回收功能
     */
    public static void testCachedThreadPool() {
        for (int i = 0; i < 10; i++) {
            final int index = i;
            ThreadUtil.getCachedThreadPool().execute(new Runnable() {

                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + index);

                }
            });
            try {
                Thread.sleep(70000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 这是一个单例线程,可以看到:所有的事务使用同一个线程
     */
    public static void testSingleThreadExecutor() {
        for (int i = 0; i < 10; i++) {
            final int index = i;
            ThreadUtil.getSingleThreadExecutor().execute(new Runnable() {

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

    /**
     * 自定义线程池测试
     */
    public static void testThreadPoolExecutor() {
        for (int i = 1; i < 20; i++) {
            System.out.println("提交第" + i + "个任务!");
            ThreadUtil.getThreadpool().execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "running=====");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "finish=====");
                }
            });
        }
    }

    public static void main(String[] args) throws Exception {
        // testFixedThreadPool();
        // testCachedThreadPool();
        // testScheduledThreadPool2();
        testSingleThreadExecutor();
    }
}
Java文档中的案例:添加了简单的暂停/恢复功能的子类

/**
 * 下面是一个添加了简单的暂停/恢复功能的子类:
 *
 * @author ChenSS
 *
 */
class PausableThreadPoolExecutor extends ThreadPoolExecutor {
    private boolean isPaused;
    private ReentrantLock pauseLock = new ReentrantLock();
    private Condition unpaused = pauseLock.newCondition();

    public PausableThreadPoolExecutor(……){
        //构造方法,需要哪个自己选择super(......)
    }

    protected void beforeExecute(Thread t, Runnable r) {
        pauseLock.lock();
        try {
            while (isPaused)
                unpaused.await();
        } catch (InterruptedException ie) {
            t.interrupt();
        } finally {
            pauseLock.unlock();
        }
        super.beforeExecute(t, r);
    }

    public void pause() {
        pauseLock.lock();
        try {
            isPaused = true;
        } finally {
            pauseLock.unlock();
        }
    }

    public void resume() {
        pauseLock.lock();
        try {
            isPaused = false;
            unpaused.signalAll();
        } finally {
            pauseLock.unlock();
        }
    }

    /**
     * 当不再引用此执行程序时,调用 shutdown。
     */
    @Override
    protected void finalize() {
        //关闭前做什么?功能自己扩展……
        super.finalize();
    }
}

posted on 2016-10-05 02:46  疯狂的妞妞  阅读(214)  评论(0编辑  收藏  举报

导航