您的位置:首页 > 编程语言 > Java开发

Java线程池实例解析

2017-07-24 15:13 316 查看

线程池概念:

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

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

         理解上述的背景后,这样我们就可以拿两者来开始类比了;码头就相当于线程池,而码头里的船就是每一条线程;为什么这样说呢,船其实是一个载体,需要执行的任务其实是过河这个动作,线程也是这样,它也是个载体,完成具体的方法是任务;首先假想一个场景,一个书生到了河边要到对岸去(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是异步非阻塞的,哪个执行完成有回调了,哪个就能输出结果;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: