Java-线程池

Tags
#Java

1.new Thread的弊端

执行一个异步任务你还只是如下new Thread吗?
new Thread(new Runnable() {

    @Override
    public void run() {
         // TODO Auto-generated method stub
    }
}).start();
那你就out太多了,new Thread的弊端如下:
  1. 每次new Thread新建对象性能差。
  1. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
  1. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
  1. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  1. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  1. 提供定时执行、定期执行、单线程、并发数控制等功能。

2.Java 自带线程池

Java通过Executors提供四种线程池,分别为:
  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  1. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  1. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  1. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
主要方法:
在未来某个时间执行给定命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由Executor决定。
启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其它作用。

2.1.newCachedThreadPool

创建一个可缓存的线程池。必要的时候创建新线程来处理请求,也会重用线程池中已经处于可用状态的线程。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程;当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
测试代码:
    /**
     * 可缓存线程池
     */
    private static void testCachedThreadPool() {
        System.out.println("这是可缓存线程池:");
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            cachedThreadPool.execute(new Runnable() {

                @Override
                public void run() {
                    print(index + "");
                }
            });
        }
    }
输出结果:
这是可缓存线程池:
1464230153368:	pool-1-thread-1:	0
1464230154372:	pool-1-thread-1:	1
1464230156376:	pool-1-thread-1:	2
1464230159380:	pool-1-thread-1:	3
1464230163384:	pool-1-thread-1:	4
1464230168386:	pool-1-thread-1:	5
1464230174391:	pool-1-thread-1:	6
1464230181395:	pool-1-thread-1:	7
1464230189397:	pool-1-thread-1:	8
1464230198400:	pool-1-thread-1:	9

2.2.newFixedThreadPool

创建固定大小的线程池,以无界队列方式运行。线程池满且线程都为活动状态的时候如果有新任务提交进来,它们会等待直到有线程可用。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。显式调用shutdown将关闭线程池。
测试代码:
    /**
     * 定长线程池
     */
    private static void testFixedThreadPool() {
        System.out.println("这是定长线程池:");
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        print(index + "");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }
    }
输出结果:
这是定长线程池:
1464229952132:	pool-1-thread-1:	0
1464229952133:	pool-1-thread-2:	1
1464229952133:	pool-1-thread-3:	2
1464229953136:	pool-1-thread-3:	3
1464229953136:	pool-1-thread-1:	4
1464229953136:	pool-1-thread-2:	5
1464229954138:	pool-1-thread-1:	6
1464229954138:	pool-1-thread-2:	8
1464229954138:	pool-1-thread-3:	7
1464229955139:	pool-1-thread-1:	9

2.3.newScheduledThreadPool

创建可定时运行(初始延时),运行频率(每隔多长时间运行,还是运行成功一次之后再隔多长时间再运行)的线程池。
特别注意: 1.初始延迟2秒后,每间隔3秒运行一次线程:
schedulePool.scheduleAtFixedRate(new MyThread(), 2, 3, TimeUnit.SECONDS);
2.初始延迟2秒后,每运行成功后再等3秒运行一次线程:
schedulePool.scheduleWithFixedDelay(new MyThread(), 2, 3, TimeUnit.SECONDS);
测试代码:
    /**
     * 周期线程池
     */
    private static void testScheduledThreadPool() {
        System.out.println("这是周期线程池:");
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                print("delay 3 seconds");
            }
        }, 2, 3, TimeUnit.SECONDS);
    }
输出结果:
这是周期线程池:
1464231196776:	pool-1-thread-1:	delay 3 seconds
1464231199776:	pool-1-thread-1:	delay 3 seconds
1464231202775:	pool-1-thread-2:	delay 3 seconds
1464231205777:	pool-1-thread-1:	delay 3 seconds
1464231208775:	pool-1-thread-3:	delay 3 seconds
1464231211775:	pool-1-thread-3:	delay 3 seconds
1464231214776:	pool-1-thread-3:	delay 3 seconds
1464231217777:	pool-1-thread-3:	delay 3 seconds
1464231220776:	pool-1-thread-3:	delay 3 seconds

2.4.newSingleThreadExecutor

创建一个单线程的线程池,以无界队列方式运行。这个线程池只有一个线程在工作(如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。)此线程池能够保证所有任务的执行顺序按照任务的提交顺序执行,同一时段只有一个任务在运行。
测试代码:
    /**
     * 单线程池
     */
    private static void testSingleThreadExecutor() {
        System.out.println("这是单线程池:");
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        print(index + "");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }
    }
输出结果:
这是单线程池:
1464230560509:	pool-1-thread-1:	0
1464230561514:	pool-1-thread-1:	1
1464230562519:	pool-1-thread-1:	2
1464230563524:	pool-1-thread-1:	3
1464230564529:	pool-1-thread-1:	4
1464230565534:	pool-1-thread-1:	5
1464230566536:	pool-1-thread-1:	6
1464230567541:	pool-1-thread-1:	7
1464230568546:	pool-1-thread-1:	8
1464230569551:	pool-1-thread-1:	9

3.自定义线程池

3.1. 四种自带线程池的实际构造方法

1.CachedThreadPool:
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
2.FixedThreadPool:
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
3.ScheduledThreadPool:
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
跳转至:
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }
4.SingleThreadExecutor:
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

3.2.ThreadPoolExecutor构造方法

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> 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;
    }
参数解释:
  1. ArrayBlockingQueue:
是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。 2. LinkedBlockingQueue: 一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。 3. SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。 4. PriorityBlockingQueue: 一个具有优先级的无限阻塞队列。 6. threadFactory(线程工厂): 用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。 7. handler(饱和策略): 当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
  1. AbortPolicy:直接抛出异常。
  1. CallerRunsPolicy:只用调用者所在线程来运行任务。
  1. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  1. DiscardPolicy:不处理,丢弃掉。当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

3.3.简单的自定义线程池

核心有两个线程,最大线程数量可无限,存活时间60s
测试代码:
    /**
     * 自定义线程池
     */
    private static void testCustomThreadPool() {
        System.out.println("这是自定义线程池:");
        ExecutorService customThreadExecutor = new ThreadPoolExecutor(
                2, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>());
        for (int i = 0; i < 10; i++) {
            final int index = i;
            customThreadExecutor.execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        print(index + "");
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }
    }
输出结果:
这是自定义线程池:
1464235380782:	pool-1-thread-1:	0
1464235380783:	pool-1-thread-2:	1
1464235381787:	pool-1-thread-2:	2
1464235381787:	pool-1-thread-1:	3
1464235382790:	pool-1-thread-2:	5
1464235382790:	pool-1-thread-1:	4
1464235383793:	pool-1-thread-1:	7
1464235383793:	pool-1-thread-2:	6
1464235384798:	pool-1-thread-1:	8
1464235384798:	pool-1-thread-2:	9
参考目录:
  1. Java:多线程,线程池,用Executors静态工厂生成常用线程池
  1. Java(Android)线程池

© fishyer 2022