一、线程池简介

线程池就是预先创建好多n个空闲线程,节省了每次使用线程时都要去创建的时间,使用时只要从线程池中取出,用完之后再还给线程池。就像现在的共享经济一样,需要的时候只要去“借”,用完之后只需还回去就行。“池”的概念都是为了节省时间而创建的。

二、Executor

 Java SE5增加了juc包来简化并发编程,而juc包中的Executor执行器来管理Thread对象。Executor在客户端和任务执行之间提供了一个间接层,与客户端直接执行任务不同,这个中介对象将执行任务。Executor允许我们管理异步执行的任务,而无须显示的管理线程的生命周期,是启动线程的优先选择。Executors 是Executor、ExecutorService、ScheduledExecutorService以及ThreadFactory、Callable的工厂及实用方法

三、线程池使用

1、newCachedThreadPool

newCachedThreadPool  会为每个任务都创建一个线程,如果有空闲线程的话也会重新使用空闲线程,如果线程没有被使用的话会在60s之后终止并从线程池中移除。它在回收旧线程时会停止创建新线程。

例子:

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int count = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.printf("thread_%d_start%n" ,count);
                }
            });
        }
        executorService.shutdown();   //用完记得关闭,要不然会一直挂起
    }
}

2、newFixedThreadPool

newFixedThreadPool 创建了一个固定数量的线程池,它重用了固定数量的线程操作一个无界队列,无论什么时候,它最多只能运行固定数量的运行中任务,当所有线程都处于活跃状态,如果有新的任务要添加进来的话,只能在队列中等待,直到有空闲线程。newFixedThreadPoll会一个运行,除非显示的调用shutdown方法。

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        for (int i = 0; i < 10; i++) {
            final int count = i;
            executorService.execute(() -> {
                System.out.printf("thread_%d_start%n" ,count);
                try {
                    Thread.sleep(3*1000);  
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
    }
}

休眠3s 是为了更能看到效果,你会发现每次会先执行5个线程,结束之后会继续执行剩余5个。

3、newSingleThreadExecutor

newSingleThreadExecutor 使用单个线程操作了一个无界队列创建了一个Executor,它保证了任务执行的有序性。
如果向newSingleThreadExecutor中提交多个任务的话,每个任务都会保证在下个任务开始之前结束,所有的任务都将使用相同的线程
public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int count = i;
            executorService.execute(() -> {
                System.out.printf("thread_%d_start%n" ,count);
                try {
                    Thread.sleep(3*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
    }
}

执行结果:

java线程池的await java中线程池_i++

 线程按照指定顺序有序的执行,说明newSingleThreadExecutor在维护着一个有序队列。

4、newScheduledThreadPool

创建了一个能够延迟或周期性执行任务的线程池。

例子:

1、延时

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 3; i++) {
            final int count = i;
            executorService.schedule(() -> {
                try {
                    Thread.sleep(1 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread_"+count);
            }, 3 , TimeUnit.SECONDS);
        }
        System.out.println("------------");
        executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
    }
}

线程启动后 会延迟3s后再执行。

2、周期性执行

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 3; i++) {
            final int count = i;
            executorService.scheduleAtFixedRate(() -> {
                System.out.println("thread_"+count);
                try {
                    Thread.sleep(3 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, 1 , 3 , TimeUnit.SECONDS);
        }
        System.out.println("------------");
//        executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
    }
}

线程池不关闭,线程延迟1s执行后,会每隔3s周期性执行。

四、线程池的关闭

线程池关闭有两个方法:

它们的原理都是通过遍历线程池中的工作任务,然后通过调用线程的interrupt方法来中断线程,如果无法响应中断的任务可能永远无法关闭。

1、void shutdown():shutdown方法会将线程池状态设置为SHUTDOWN,然后中断线程池中没有正在执行的线程,新添加进来的任务将不会再被接受。

2、List<Runnable> shutdownNow():shutdownNow方法会将线程池状态设置为STOP,然后尝试停止正在执行或者暂停任务的线程,并返回等待执行任务的列表。