Java线程池深度解析:四种创建方式详解

Java线程池深度解析:四种创建方式详解

  1. 后端开发与架构
  2. 2016.02.05
  3. 7 min read

总结线程池的使用方式

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

  1. newCachedThreadPool 创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程.
  2. newFixedThreadPool 创建一个定长线程池, 可控制线程最大并发数, 超出的线程会在队列中等待.
  3. newScheduledThreadPool 创建一个定长线程池, 支持定时及周期性任务执行.
  4. newSingleThreadExecutor 创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序 (FIFO, LIFO, 优先级) 执行.

线程池比较单线程的优势在于:

  • 重用存在的线程, 减少对象创建、消亡的开销, 性能佳.
  • 可有效控制最大并发线程数, 提高系统资源的使用率, 同时避免过多资源竞争, 避免堵塞.
  • 提供定时执行、定期执行、单线程、并发数控制等功能.

newCachedThreadPool

public static void main(String[] args) {
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cachedThreadPool.execute(new Runnable() {
            public void run() {
                System.out.println(index);
            }
        });
    }
}
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

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

这里的线程池是无限大的, 当一个线程完成任务之后, 这个线程可以接下来完成将要分配的任务, 而不是创建一个新的线程,

java api 1.7 will reuse previously constructed threads when they are available.

newFixedThreadPool

public static void main(String[] args) {
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 10; i++) {
        final int index = i;
        fixedThreadPool.execute(new Runnable() {
            public void run() {
                try {
                    System.out.println(index);
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

创建一个定长线程池, 可控制线程最大并发数, 超出的线程会在队列中等待

定长线程池的大小最好根据系统资源进行设置. 如 Runtime.getRuntime().availableProcessors()

public static void main(String[] args) {
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    for (int i = 0; i < 10; i++) {
        scheduledThreadPool.schedule(new Runnable() {
            public void run() {
                System.out.println("delay 3 seconds");
            }
        }, 3, TimeUnit.SECONDS);
    }
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

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

newSingleThreadExecutor

public static void main(String[] args) {
    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        singleThreadExecutor.execute(new Runnable() {
            public void run() {
                try {
                    System.out.println(index);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

按顺序来执行线程任务 但是不同于单线程, 这个线程池只是只能存在一个线程, 这个线程死后另外一个线程会补上

/**
 * ThreadPoolExecutor 类的使用方法
 * 实现高并发: 在线程类中的 run()方法内设置 Thread.sleep(long delta); delta 取值为: (并发开始时间戳 - 线程开始时间戳)
 * Created by Administrator on 2016/11/19.
 */
public class ThreadPoolExecutorTest {
    public static void main(String[] args) {

        // 设置核心池大小
        int corePoolSize = 5;

        // 设置线程池最大能接受多少线程

        // 当前线程数大于 corePoolSize、小于 maximumPoolSize 时, 超出 corePoolSize 的线程数的生命周期
        long keepActiveTime = 200;

        // 设置时间单位, 秒
        TimeUnit timeUnit = TimeUnit.SECONDS;

        // 设置线程池缓存队列的排队策略为 FIFO, 并且指定缓存队列大小为 5
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(5);

        // 创建 ThreadPoolExecutor 线程池对象, 并初始化该对象的各种参数
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepActiveTime, timeUnit,workQueue);

        // 往线程池中循环提交线程
        for (int i = 0; i < 15; i++) {
            // 创建线程类对象
            MyTask myTask = new MyTask(i);
            // 开启线程
            executor.execute(myTask);
            // 获取线程池中线程的相应参数
            System.out.println("线程池中线程数目: " +executor.getPoolSize() + ", 队列中等待执行的任务数目: "+executor.getQueue().size() + ", 已执行完的任务数目: "+executor.getCompletedTaskCount());
        }
        // 待线程池以及缓存队列中所有的线程任务完成后关闭线程池.
        executor.shutdown();
    }
}
/**
 * 线程类
 */
class MyTask implements Runnable {
    private int num;

    public MyTask(int num) {
        this.num = num;
    }

    @Override
    public void run() {
        System.out.println("正在执行task " + num );
        try {
            Thread.currentThread().sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task " + num + "执行完毕");
    }

    /**
     * 获取(未来时间戳 - 当前时间戳)的差值,
     * 也即是: (每个线程的睡醒时间戳 - 每个线程的入睡时间戳)
     * 作用: 用于实现多线程高并发
     * @return
     * @throws ParseException
     */
    public long getDelta() throws ParseException {
        // 获取当前时间戳
        long t1 = new Date().getTime();
        // 获取未来某个时间戳(自定义, 可写入配置文件)
        String str = "2016-11-11 15:15:15";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long t2 = simpleDateFormat.parse(str).getTime();
        return t2 - t1;
    }
}
Java 并发编程 学习笔记