线程池ThreadPoolExecutor与Executors框架

一、线程池的优点

降低资源损耗、提高响应速度、提高线程的可管理性

二、ThreadPoolExecutor线程池

  1. ThreadPoolExecutor的重要属性说明:

 corePoolSize:核心线程数,如果运行的线程少于corePoolSize,则创建新线程来执行新任务,即使线程池中存在空闲线程
 maximumPoolSize:最大线程数,可允许创建的线程数,线程数量达到核心线程数后,先放进队列,如果队列满了,则创建新线程
 keepAliveTime:如果线程数多于corePoolSize,则这些多余的线程的空闲时间超过keepAliveTime时将被终止
 unit:keepAliveTime参数的时间单位
 workQueue:保存任务的阻塞队列,与线程池的大小有关:
                     当运行的线程数少于corePoolSize时,在有新任务时直接创建新线程来执行任务而无需再进队列
                     当运行的线程数等于或多于corePoolSize,在有新任务添加时则选加入队列,不直接创建线程
                     当队列满时,在有新任务时就创建新线程
 threadFactory:使用ThreadFactory创建新线程,默认使用defaultThreadFactory创建线程
 handle:定义处理被拒绝任务的策略,默认使用ThreadPoolExecutor.AbortPolicy,任务被拒绝时将抛出RejectExecutorException

UML图: 

                     wgs_93这图有点丑.....

 

三、Executors框架

Executors是一个比较灵活的异步执行框架。Executors提供了多个构造方法。

1、单个线程的线程池构造方法

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

重上面的构造方法参数中可见,该线程池的核心线程数和最大线程数都为1。使用的队列是FIFO的阻塞队列。线程池中的某个线程由于异常而结束时,线程池就会创建一条线程继续执行后面的任务。

package com.scott.current.executor;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。
 * 如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
 */
public class SingleThreadExecutorTest {
    public static void main(String[] args) {

        final AtomicInteger threadNumber = new AtomicInteger(1);

        ThreadFactory factory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(new ThreadGroup("测试线程组"), r, "测试线程" + threadNumber.getAndIncrement(), 0);
            }
        };
        ExecutorService executor = Executors.newSingleThreadExecutor(factory);
        // execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否
        IntStream.range(0, 5).forEach(i -> executor.execute(() -> {
            System.out.println(Thread.currentThread().getName());
        }));
        /**
         * submit()方法用于提交需要返回值的任务。
         * 线程池会返回一个future类型的对象,future的get()方法来获取返回值
         * future的get(long timeout,TimeUnit unit)会阻塞
         */
        IntStream.range(0, 5).forEach(i -> {
            Future<?> submit = executor.submit(() -> {
                System.out.println(Thread.currentThread().getName());
                return "money";
            });
            try {
                System.out.println("返回结果:" + submit.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        try {
            //close pool
            executor.shutdown();
            executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (!executor.isTerminated()) {
                executor.shutdownNow();
            }
        }
    }
}

测试结果:

 

2、可缓存的线程池

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

在执行新任务时,如果线程池中有空闲线程,则使用空闲线程,否则创建新线程。使用原线程可以避免创建线程带来的性能消耗。CachedThreadPool是一个会根据需要创建新线程的线程池。corePoolSize被设置为0。maximumPoolSize被设置为Integer.MAX.VALUE,即它是无界的,这也就意味着如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新的线程。这种可能会比较适用在消费者的消费能力大于生产者的生产能力的场景中。

package com.scott.current.executor;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * 创建可缓存的线程池,如果线程池中的线程在60秒未被使用就将被移除
 * 在执行新的任务时,当线程池中有之前创建的可用线程就重用可用线程,否则就新建一条线程
 */
public class CachedThreadExecutorTest {
    public static void main(String[] args) {
        final AtomicInteger threadNumber = new AtomicInteger(1);
        ThreadFactory factory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(new ThreadGroup("测试线程组"), r, "测试线程"+ threadNumber.getAndIncrement(), 0);
            }
        };
        // 如果线程未被移除并且空闲,则线程可以重用,线程数量会动态变化
        ExecutorService service = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(),
                factory);
        ExecutorService executorService = Executors.newCachedThreadPool(factory);
        // 执行 5 次任务
        IntStream.range(0, 5).forEach(i -> {
            executorService.execute(() -> {
                System.out.println("线程名称:" + Thread.currentThread().getName() + "\t任务名:" + i);
            });
        });
        try {
            //close pool
            executorService.shutdown();
            executorService.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (!executorService.isTerminated()) {
                executorService.shutdownNow();
            }
        }
    }
}

测试结果:

 

3、可重用固定线程数的线程池

  public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

如上所示,当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize。线程池中的某个线程由于异常而结束时,线程池就会再补充一条新线程。如果线程池中所有的线程都处于活动状态,此时提交的任务就在队列中等待,直到有可用的线程。

package com.scott.current.executor;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * newFixedThreadPool:创建可重用且固定线程数的线程池,
 * 如果线程池中的所有线程都处于活动状态,此时再提交任务就在队列中等待,直到有可用线程
 * 如果线程池中的某个线程由于异常而结束时,线程池就会再补充一条新线程。
 */
public class FixedThreadExecutorTest {

    public static void main(String[] args) {

        final AtomicInteger threadNumber = new AtomicInteger(1);

        ThreadFactory factory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(new ThreadGroup("测试线程组"), r, "测试线程" + threadNumber.getAndIncrement(), 0);
            }
        };
        // LinkedBlockingQueue 一个基于FIFO排序的阻塞队列,在所有corePoolSize线程都忙时新任务将在队列中等待
        ExecutorService fixedThreadPool = new ThreadPoolExecutor(3, 3,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(), factory);

        ExecutorService executor = Executors.newFixedThreadPool(3, factory);

        IntStream.range(0, 6).forEach(i -> executor.execute(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                String threadName = Thread.currentThread().getName();
                System.out.println("finished: " + threadName);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));
        try {
            //close pool
            executor.shutdown();
            executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (!executor.isTerminated()) {
                executor.shutdownNow();
            }
        }
    }
}

测试结果:

 

4、延时线程池

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

initialDelay:多长时间后开始执行,period: 第一次执行后每隔多长时间执行一次,unit:时间单位。这种适合每天的定时任务执行。如果只执行一定次数的未必合适。

package com.scott.current.executor;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * 延时线程池
 */
public class ScheduledThreadExecutorTest {
    public static void main(String[] args) {

        final AtomicInteger threadNumber = new AtomicInteger(1);

        ThreadFactory factory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(new ThreadGroup("测试线程组"), r, "测试线程" + threadNumber.getAndIncrement(), 0);
            }
        };
//        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(3, factory);
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3, factory);
        Runnable r = () ->{
            System.out.println("名称为:" + Thread.currentThread().getName()+"的线程正在执行定时任务");
        };
        // 1 秒后开始执行,然后没3秒执行一次
        IntStream.range(0, 4).forEach(i -> {
            executorService.scheduleAtFixedRate(r, 1, 3, TimeUnit.SECONDS);
        });
        try {
            executorService.shutdown();
            executorService.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (!executorService.isTerminated()) {
                executorService.shutdownNow();
            }
        }
    }
}

测试结果:

Executors框架的其它方法介绍:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值