您的位置:首页 > 移动开发 > Android开发

android 线程池的使用以及Executors的优缺点

2017-12-06 16:59 453 查看
android开发,大家最熟悉的肯定是主线程,也就是ui线程,也都知道在非ui线程更新界面会报错提示不允许在子线程更新ui。但是耗时操作还是需要使用子线程,例如:

new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();


感觉方便直接,在任务结束后GC也会自动回收该线程,不过它还在存在很大的弊端

如果某个地方需要开启大量的线程。创建线程和销毁线程是需要时间的,这样会导致性能不足

线程无法管理,相互竞争导致卡顿或者oom

功能太过单一

所以针对这些问题,我们需要重用线程,使用线程池。

线程池:ThreadPoolExecutor

先来看看构造函数:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)


corepoolsize:核心线程数

maximumPoolSize:最大线程数

keepAliveTime:线程空闲时间

unit:keepAliveTime的时间单位

workQueue:阻塞任务队列

threadFactory:新建线程工厂

RejectedExecutionHandler:当提交当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

配置起来还是比较繁琐,所以官方提供了一个类Executors来帮助我们迅速创建我们需要的线程池,比较常见的有:

1:newFixedThreadPool() :

该方法返回一个固定线程数量的线程池,该线程池中的线程数量始终不变,即不会再创建新的线程,也不会销毁已经创建好的线程,自始自终都是那几个固定的线程在工作,所以该线程池可以控制线程的最大并发数。 超出的线程会在队列中等待。

2:newCachedThreadPool:

该方法返回一个可以根据实际情况调整线程池中线程的数量的线程池。如果没有线程不够用则会一直创建,有空闲线程则会复用。

3:newSingleThreadExecutor() :

顾名思义,返回只有一个线程的线程池。多余的任务会存在队列中等待执行

4:newScheduledThreadPool():

返回一个可以固定线程个数和设置线程延迟执行时间,执行周期 的线程池

好了,来看下具体使用方式:

newFixedThreadPool

ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 100; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
Log.i("thread", Thread.currentThread().getName());
}
});
}


规定线程数为3,然后循环执行打印线程名字

12-06 16:29:06.665 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2
12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3
12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3
12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1


可以看出始终只有三个线程在跑任务,符合预期

newCachedThreadPool

ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
Log.i("thread", Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}


代码差不多,加了一个睡眠时间模拟处理时间,看看控制台部分输出:

12-06 16:33:28.463 15429-15454/? I/thread: pool-1-thread-11
12-06 16:33:28.463 15429-15456/? I/thread: pool-1-thread-13
12-06 16:33:28.464 15429-15457/? I/thread: pool-1-thread-14
12-06 16:33:28.464 15429-15458/? I/thread: pool-1-thread-15
12-06 16:33:28.464 15429-15455/? I/thread: pool-1-thread-12
12-06 16:33:28.464 15429-15468/? I/thread: pool-1-thread-25
12-06 16:33:28.464 15429-15469/? I/thread: pool-1-thread-26
12-06 16:33:28.465 15429-15470/? I/thread: pool-1-thread-27
12-06 16:33:28.465 15429-15471/? I/thread: pool-1-thread-28
12-06 16:33:28.466 15429-15472/? I/thread: pool-1-thread-29
12-06 16:33:28.466 15429-15474/? I/thread: pool-1-thread-30
12-06 16:33:28.467 15429-15476/? I/thread: pool-1-thread-32
12-06 16:33:28.467 15429-15475/? I/thread: pool-1-thread-31
12-06 16:33:28.467 15429-15477/? I/thread: pool-1-thread-33
12-06 16:33:28.467 15429-15478/? I/thread: pool-1-thread-34
12-06 16:33:28.468 15429-15479/? I/thread: pool-1-thread-35
12-06 16:33:28.469 15429-15480/? I/thread: pool-1-thread-36
12-06 16:33:28.469 15429-15481/? I/thread: pool-1-thread-37
12-06 16:33:28.469 15429-15482/? I/thread: pool-1-thread-38
12-06 16:33:28.471 15429-15484/? I/thread: pool-1-thread-40
12-06 16:33:28.471 15429-15483/? I/thread: pool-1-thread-39


因为睡眠时间长导致每次循环都创建一个线程,如果取消sleep时间会看到线程数量没有这么多,因为会复用。

newSingleThreadExecutor

ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 100; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
Log.i("thread", Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}


还是差不多的代码,看控制台输出

12-06 16:36:30.480 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:31.481 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:32.481 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:33.482 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:34.482 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:35.483 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:36.483 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:37.484 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:38.484 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:39.484 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:40.485 15693-15708/? I/thread: pool-1-thread-1
12-06 16:36:41.485 15693-15708/? I/thread: pool-1-thread-1


可以看出每次都是同一个线程在执行,所有任务在排队执行

newScheduledThreadPool

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
executorService.schedule(new Runnable() {
@Override
public void run() {
Log.i("thread", Thread.currentThread().getName());
}
}, 3000, TimeUnit.MILLISECONDS);


延迟执行,schedule第二三个方法执行时间长度和时间单位

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.i("thread", Thread.currentThread().getName());
}
}, 5000,3000, TimeUnit.MILLISECONDS);


周期性执行,延迟5秒后,每隔3秒执行一次,按照任务开始计算,然后任务执行时间大于间隔时间,则会在任务结束后立马执行下一次。scheduleWithFixedDelay会在任务结束后开始计算间隔时间,和任务执行时间无关.

简单介绍了一下好处,也得来讲讲这种简单创建线程池的方式的缺点:

newFixedThreadPool和newSingleThreadExector:

主要问题是推挤的请求处理队列可能会耗费非常大的内存,甚至导致oom。

newCachedThreadPool和newScheduledThreadPool:

主要问题是线程数没有限制,可能会创建数量非常多的线程,导致oom
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程池 android开发