Java线程池实例解析

线程池概念:

         首先,要理解什么是线程池;还是先拿具体例子来说,这个例子可能比较偏了,因为现实生活中接触的少了,但是比较能形象的阐述线程池的含义;

         摆渡,在科技不发达的时候,没有能力造那种大桥,人们过河只能通过码头的船夫摆渡到对岸;为了更贴切,我说的这个码头就只做摆渡的生意,其他运输之类的不干;码头的船则是码头的老板创业时自己造的;

         理解上述的背景后,这样我们就可以拿两者来开始类比了;码头就相当于线程池,而码头里的船就是每一条线程;为什么这样说呢,船其实是一个载体,需要执行的任务其实是过河这个动作,线程也是这样,它也是个载体,完成具体的方法是任务;首先假想一个场景,一个书生到了河边要到对岸去(ps:不管他会不会游泳,还是会飞;这里限定他只能通过船这个途径);那他只有两种选择,第一个是自己造船,第二个是坐码头里摆渡的船;这两种方式不管是哪个都是完成过河的任务,但是你会发现,码头的船很多,当书生坐上船走后,后面又来了几个人也要过河,他们能立马就载上这几个人;

         码头是船的集合,那么线程池就是线程的集合;为什么要弄出个线程池出来呢,差异就显而易见了,码头的船在客户来之前就已经造好放在那儿等待使用,而如果没有码头的话,客户来到这儿要过河只能自己造船,然后才能过河;这是其一,一个要建造船,一个不要,线程池也是这样,如果没有线程池的话,那么某个方法中要使用多线程的时候,需要new一个线程,而线程池则不一样了,当需要使用线程的时候直接去线程池中取一个,省去了创建线程这个环节,提高效率;

         还有就是多个人需要过河,码头有很多船则会大大的提高效率,不仅省去了造船的环节还省去了资源的占用,如果是每个人自己造船的话,岸边的树估计要被砍光了;多个任务需要执行的时候,手动创建线程会占用太多内存资源,而且每个都需要new,浪费了时间;

         好了,其实上述的例子已经把线程池以及它的优点都基本表达出来了,但是还是要结合代码具体讲解下,才能更清楚理解;

线程池:
         创建一个线程集合,池子的大小取决于内存数量;当需要执行一个任务的时候是重用一个等待的线程,而不是重新开启一个线程;当任务执行完成后继续回到池子中等待下一个任务

优点:

         1、减少在创建和销毁线程上所花的时间和资源开销
         2、与主线程隔离,实现异步执行
         注意:池中的线程数不是越多越好,线程休眠同样也会占用资源,所以要合理的选择线程池大小

线程池的几个实例:
基础类:

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by zelei.fan on 2017/6/13.
 */
public class ThreadHandle implements Runnable{

    private String index;

    ThreadHandle(String index){
        this.index = index;
    }

    @Override
    public void run() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        String time = df.format(date);
        System.out.println(Thread.currentThread().getName()+"Start Time"+time);
        /*中间一段处理时间*/
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Date date1 = new Date();
        String time1 = df.format(date1);
        System.out.println(Thread.currentThread().getName()+"End Time"+time1);
    }
}

1、Executors.newCachedThreadPool();可缓存的线程池,当线程越多线程池规模也随之扩大,默认超时时间是60s,如果超过会自动终止该线程

public static void main(String[] args) {
        /*newCachedThreadPool可缓存线程池同时启动了5个线程来执行*/
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i ++){
            pool.execute(new ThreadHandle(String.valueOf(i)));
        }
    }
运行结果:

pool-1-thread-3Start Time2017-07-24 13:35:28
pool-1-thread-1Start Time2017-07-24 13:35:28
pool-1-thread-5Start Time2017-07-24 13:35:28
pool-1-thread-4Start Time2017-07-24 13:35:28
pool-1-thread-2Start Time2017-07-24 13:35:28
pool-1-thread-2End Time2017-07-24 13:35:32
pool-1-thread-3End Time2017-07-24 13:35:32
pool-1-thread-4End Time2017-07-24 13:35:32
pool-1-thread-1End Time2017-07-24 13:35:32
pool-1-thread-5End Time2017-07-24 13:35:32

可以看到线程池同时启动了5个线程来执行任务,理论上可开启的线程数是无限的,但是手cpu的影响,cpu越多的话开的越多;

2、Executors.newFixedThreadPool(2);创建一个固定大小的线程池;当线程达到最大数时,大小不再变化,适用于固定的稳定的并发编程

public static void main(String[] args) {
        /*newFixedThreadPool固定线程数的线程池同一时刻最多只开启设定的5个线程,只有当前面的线程结束后才会继续执行后面等待的任务*/
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 6; i ++){
            executorService.execute(new ThreadHandle(String.valueOf(i)));
        }
    }
运行结果:

pool-1-thread-2Start Time2017-07-24 13:42:04
pool-1-thread-1Start Time2017-07-24 13:42:04
pool-1-thread-1End Time2017-07-24 13:42:08
pool-1-thread-2End Time2017-07-24 13:42:08
pool-1-thread-1Start Time2017-07-24 13:42:08
pool-1-thread-2Start Time2017-07-24 13:42:08
pool-1-thread-1End Time2017-07-24 13:42:12
pool-1-thread-2End Time2017-07-24 13:42:12
pool-1-thread-1Start Time2017-07-24 13:42:12
pool-1-thread-2Start Time2017-07-24 13:42:12
pool-1-thread-2End Time2017-07-24 13:42:16
pool-1-thread-1End Time2017-07-24 13:42:16

同时开启是个任务来执行,但是线程池大小只有2,一个时间点只能有两个线程并行,其余的任务则必须等待,直到上一个任务执行完成;

3、Executors.newSingleThreadExecutor();创建一个单线程,串行执行

public static void main(String[] args) {
        /*newSingleThreadExecutor单线程,同一时刻只有一个线程实例,所有任务都用这个实例执行
        * 相当于Executors.newFixedThreadPool(1);
        * */
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i ++){
            singleThreadExecutor.execute(new ThreadHandle(String.valueOf(i)));
        }
    }
运行结果:

pool-1-thread-1Start Time2017-07-24 13:46:07
pool-1-thread-1End Time2017-07-24 13:46:11
pool-1-thread-1Start Time2017-07-24 13:46:11
pool-1-thread-1End Time2017-07-24 13:46:15
pool-1-thread-1Start Time2017-07-24 13:46:15
pool-1-thread-1End Time2017-07-24 13:46:19
pool-1-thread-1Start Time2017-07-24 13:46:19
pool-1-thread-1End Time2017-07-24 13:46:23
pool-1-thread-1Start Time2017-07-24 13:46:23
pool-1-thread-1End Time2017-07-24 13:46:27

上述结果中至始至终就只有一个线程thread-1在运行;

4、Executors.newScheduledThreadPool(5);计划类线程池
1)、延时执行:
public static void main(String[] args) {
        /*计划类线程池*/
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 5; i ++){
            /*延时10秒执行*/
            scheduledExecutorService.schedule(new ThreadHandle(String.valueOf(i)), 10, TimeUnit.SECONDS);
        }
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        String time = df.format(date);
        System.out.println(time);
    }

打印结果:

2017-07-24 13:54:10
pool-1-thread-2Start Time2017-07-24 13:54:20
pool-1-thread-3Start Time2017-07-24 13:54:20
pool-1-thread-1Start Time2017-07-24 13:54:20
pool-1-thread-4Start Time2017-07-24 13:54:20
pool-1-thread-5Start Time2017-07-24 13:54:20
pool-1-thread-3End Time2017-07-24 13:54:24
pool-1-thread-2End Time2017-07-24 13:54:24
pool-1-thread-4End Time2017-07-24 13:54:24
pool-1-thread-1End Time2017-07-24 13:54:24
pool-1-thread-5End Time2017-07-24 13:54:24

可以看到线程在十秒之后才开始执行;
2)、间断执行:

public static void main(String[] args) {
        /*计划类线程池*/
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 5; i ++){
            /*从0ms开始每隔10秒执行一个任务,需要注意的是这个时间间隔是从上一个线程开始时计算的*/
            scheduledExecutorService.scheduleAtFixedRate(new ThreadHandle(String.valueOf(i)), 0, 10, TimeUnit.SECONDS);
        }
    }
打印结果:

pool-1-thread-1Start Time2017-07-24 13:59:17
pool-1-thread-5Start Time2017-07-24 13:59:17
pool-1-thread-2Start Time2017-07-24 13:59:17
pool-1-thread-4Start Time2017-07-24 13:59:17
pool-1-thread-3Start Time2017-07-24 13:59:17
pool-1-thread-1End Time2017-07-24 13:59:21
pool-1-thread-4End Time2017-07-24 13:59:21
pool-1-thread-5End Time2017-07-24 13:59:21
pool-1-thread-3End Time2017-07-24 13:59:21
pool-1-thread-2End Time2017-07-24 13:59:21
pool-1-thread-4Start Time2017-07-24 13:59:27
pool-1-thread-1Start Time2017-07-24 13:59:27
pool-1-thread-5Start Time2017-07-24 13:59:27
pool-1-thread-3Start Time2017-07-24 13:59:27
pool-1-thread-2Start Time2017-07-24 13:59:27

线程每次开始于上一次开始间隔都是10s,而且会无限的执行下去,类似于定时任务;

3)、间断执行:

public static void main(String[] args) {
        /*计划类线程池*/
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 5; i ++){
            /*从0ms开始每隔10秒执行一个任务,需要注意的是这个时间间隔是从上一个线程结束后计算的*/
            scheduledExecutorService.scheduleWithFixedDelay(new ThreadHandle(String.valueOf(i)), 0, 10, TimeUnit.SECONDS);
        }
    }
打印结果:

pool-1-thread-5Start Time2017-07-24 14:02:49
pool-1-thread-3Start Time2017-07-24 14:02:49
pool-1-thread-2Start Time2017-07-24 14:02:49
pool-1-thread-4Start Time2017-07-24 14:02:49
pool-1-thread-1Start Time2017-07-24 14:02:49
pool-1-thread-3End Time2017-07-24 14:02:53
pool-1-thread-2End Time2017-07-24 14:02:53
pool-1-thread-5End Time2017-07-24 14:02:53
pool-1-thread-4End Time2017-07-24 14:02:53
pool-1-thread-1End Time2017-07-24 14:02:53
pool-1-thread-3Start Time2017-07-24 14:03:03
pool-1-thread-5Start Time2017-07-24 14:03:03
pool-1-thread-4Start Time2017-07-24 14:03:03
pool-1-thread-1Start Time2017-07-24 14:03:03
pool-1-thread-2Start Time2017-07-24 14:03:03

上面结果是每次线程和上一次线程执行结束后间隔10s;

多线程回调:
基础类:

import java.util.concurrent.Callable;

/**
 * Created by zelei.fan on 2017/6/13.
 */
public class CallableTest implements Callable<String> {

    private int index;

    public CallableTest(int index){
        this.index = index;
    }

    @Override
    public String call() throws Exception {
        System.out.println("call()方法被自动调用, 开始*******" + Thread.currentThread().getName());
        if(index > 5){
            Thread.sleep(1000);
        }else {
            Thread.sleep(10000);
        }
        return "call()方法被自动调用,结束*******" + Thread.currentThread().getName();
    }
}


submit:将线程放入线程池中,除了使用execute,还可以使用submit,而且能够获取回调,submit适用于生产者-消费者模式,和Future一起使用起到没有返回结果就阻塞当前线程,等待线程池返回结果;
public static void main(String[] args) {
        ExecutorService pool1 = Executors.newCachedThreadPool();
        List<Future<String>> futureList = Lists.newArrayList();
        for (int i = 0; i < 5; i ++){
            Future<String> future = pool1.submit(new CallableTest(i));
            futureList.add(future);
        }
        futureList.forEach(new Consumer<Future<String>>() {
            @Override
            public void accept(Future<String> stringFuture) {
                try {
                    System.out.println(stringFuture.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        });
    }
打印结果:

call()方法被自动调用, 开始*******pool-1-thread-1
call()方法被自动调用, 开始*******pool-1-thread-4
call()方法被自动调用, 开始*******pool-1-thread-5
call()方法被自动调用, 开始*******pool-1-thread-3
call()方法被自动调用, 开始*******pool-1-thread-2
call()方法被自动调用,结束*******pool-1-thread-1
call()方法被自动调用,结束*******pool-1-thread-2
call()方法被自动调用,结束*******pool-1-thread-3
call()方法被自动调用,结束*******pool-1-thread-4
call()方法被自动调用,结束*******pool-1-thread-5

调用结束打印的语句都是在run方法中返回出来的,通过future来接受返回参数,然后将future打印;

说到回调,还有一种就是CompletionService,区别是future是阻塞的,而CompletionService是异步非阻塞的;
看下具体例子:

public static void main(String[] args) {
        ExecutorService pool1 = Executors.newCachedThreadPool();
        CompletionService service = new ExecutorCompletionService(pool1);
        for (int i = 0; i < 10; i ++){
            service.submit(new CallableTest(i));
        }
        for(int i = 0; i < 10; i ++){
            try {
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = new Date();
                String time = df.format(date);
                System.out.println(service.take().get() + time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
打印结果:

call()方法被自动调用, 开始*******pool-1-thread-1
call()方法被自动调用, 开始*******pool-1-thread-5
call()方法被自动调用, 开始*******pool-1-thread-6
call()方法被自动调用, 开始*******pool-1-thread-2
call()方法被自动调用, 开始*******pool-1-thread-9
call()方法被自动调用, 开始*******pool-1-thread-10
call()方法被自动调用, 开始*******pool-1-thread-3
call()方法被自动调用, 开始*******pool-1-thread-7
call()方法被自动调用, 开始*******pool-1-thread-4
call()方法被自动调用, 开始*******pool-1-thread-8
call()方法被自动调用,结束*******pool-1-thread-72017-07-24 15:10:00
call()方法被自动调用,结束*******pool-1-thread-102017-07-24 15:10:01
call()方法被自动调用,结束*******pool-1-thread-92017-07-24 15:10:01
call()方法被自动调用,结束*******pool-1-thread-82017-07-24 15:10:01
call()方法被自动调用,结束*******pool-1-thread-62017-07-24 15:10:01
call()方法被自动调用,结束*******pool-1-thread-52017-07-24 15:10:10
call()方法被自动调用,结束*******pool-1-thread-22017-07-24 15:10:10
call()方法被自动调用,结束*******pool-1-thread-12017-07-24 15:10:10
call()方法被自动调用,结束*******pool-1-thread-32017-07-24 15:10:10
call()方法被自动调用,结束*******pool-1-thread-42017-07-24 15:10:10

         通过上面一个例子可以知道Future是阻塞取出结果的,按顺序执行,如果说正常的前面的线程执行完成后面的线程还在执行中的话,前面线程的结果时可以直接返回的,但是如果后面的线程比前面的线程先执行完成,则后面线程的返回结果需要等待前面线程返回后才能取得结果; 而CompletionService是异步非阻塞的,哪个执行完成有回调了,哪个就能输出结果;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值