1.介绍

多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间,当T1 + T3 远大于 T2,则可以采用线程池,可以提高服务器性能。
在Java 5之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。

2.线程池的好处

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

3.Executor框架

Java线程池多线程存储数据 java多线程 线程池_线程池



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

3.1 newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

public class TestThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new MyThread());
        }
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程正在被调用!");
    }
}

结果:

pool-1-thread-2线程正在被调用!
pool-1-thread-3线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-2线程正在被调用!
pool-1-thread-3线程正在被调用!
pool-1-thread-3线程正在被调用!
pool-1-thread-4线程正在被调用!
pool-1-thread-3线程正在被调用!
pool-1-thread-4线程正在被调用!

发现该线程池会被重复使用,现在我们将每一个线程run方法加一个sleep方法:

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程正在被调用!");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

此时运行结果:

pool-1-thread-1线程正在被调用!
pool-1-thread-2线程正在被调用!
pool-1-thread-3线程正在被调用!
pool-1-thread-4线程正在被调用!
pool-1-thread-5线程正在被调用!
pool-1-thread-6线程正在被调用!
pool-1-thread-7线程正在被调用!
pool-1-thread-8线程正在被调用!
pool-1-thread-9线程正在被调用!
pool-1-thread-10线程正在被调用!

说明当空闲线程不够时,可以创建显得线程。查看源码发现可创建最大线程数量为 @Native public static final int MAX_VALUE = 0x7fffffff;也就是不超过虚拟机内存是可以无限创建线程的,当然也只是理论上。

3.2 newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

public class TestThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new MyThread());
        }
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程正在被调用!");
    }
}

结果:

pool-1-thread-1线程正在被调用!
pool-1-thread-2线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-2线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-2线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-2线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-2线程正在被调用!

此时线程池数量固定,没有可执行的线程的时候,就等待,不会创建新的线程的。

3.3 newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

public class TestThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 10; i++) {
            executorService.execute(new MyThread());
        }
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程正在被调用!");
    }
}

结果:

pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!
pool-1-thread-1线程正在被调用!

3.4 newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

public class TestThreadPool {

    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

        scheduledThreadPool.schedule(new MyThread(), 0, TimeUnit.SECONDS);

        scheduledThreadPool.schedule(new MyThread(), 5, TimeUnit.SECONDS);
    }
}

class MyThread implements Runnable {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void run() {
        System.out.println(sf.format(new Date()) + " : " + Thread.currentThread().getName() + "线程正在被调用!");
    }
}

结果:

2017-03-24 15:00:10 : pool-1-thread-1线程正在被调用!
2017-03-24 15:00:15 : pool-1-thread-2线程正在被调用!