通俗的讲就是存放和管理线程的一个池子,那这个池子出现有什么好处呢?在讲之前我们先来了解一些定义:
进程:是系统进行资源分配和调度的一个独立单位,它包括独立的地址空间、系统资源和1-n个线程,进程间切换开销大并且不能共享资源,进程间互不影响
线程:是CPU调度和分派的基本单位,必须依附于进程而存在,线程间切换开销小但共享所在进程的全部资源,一个线程挂掉会导致所在进程挂掉
并发:是多个任务交替使用CPU,同一时刻还是只有一个任务在运行,也就是各个代码块交替执行
并行:是多个任务同时在运行互不影响,也就是各个代码块同时执行
线程的运行分为五个阶段:创建、就绪、运行、阻塞、终止。
简单的说就是手机上装的QQ,微信、迅雷等每个应该程序都至少有一个进程,而像迅雷里可同时下载多部电影这就是线程干的活了。所以也就得出结论了一个程序至少有一个进程,一个进程至少有一个线程。
创建线程的两张方式:
由于java只允许单继承,所以如果某个类需要继承其他类,就比较麻烦,实现Runnable接口不存在这样的问题,并且用Runnable接口创建的线程可以共同处理同一份资源而用Thread创建的线程都是各自处理自己的资源。既然实现Runnable接口创建的线程也还不错,那为什么要使用线程池呢?那下面就来对比下:
new Thread()的缺点
线程池的优点
使用线程池就肯定会用到ThreadPoolExecutor类,下面就通过它的构造来说明一下
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory)
下面简单的对workQueue的排队策略和handler拒绝策略做个介绍:
workQueue的几种排队策略:
public boolean isEmpty() {
return true;
}
public int size() {
return 0;
}
handler拒绝策略:
当一个新任务提交到线程池后,线程池处理任务遵循的规则是:
注意:线程池回收线程时,对所谓的核心线程和非核心线程是一视同仁,直到线程池中的线程数量等于核心线程数量时,回收过程才会停止。
private void btn1Click() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.SECONDS,
new LinkedBlockingDeque(2));
for (int i = 0; i < 4; i++) {
final int temp = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
Log.d("run:", "" + temp);
}
};
executor.execute(runnable);
}
}
当核心线程数2,任务队列2,最大线程数6时,任务队列为链表结构时,当循环次数i<4时,线程的执行应该是先创建两个核心线程,执行任务,剩下的放到任务队列,待核心线程空闲时再执行任务队列的任务,所以任务0、1永远在任务2、3之前运行。打印结果如下:
10-11 14:41:41.483 24546-24604/com.cn.liuyz.threadpooldemo D/run:: 0
10-11 14:41:41.484 24546-24605/com.cn.liuyz.threadpooldemo D/run:: 1
10-11 14:41:42.483 24546-24604/com.cn.liuyz.threadpooldemo D/run:: 2
10-11 14:41:42.484 24546-24605/com.cn.liuyz.threadpooldemo D/run:: 3
当把循环次数改成i<5时,则任务0、1、4永远先执行,但顺序不确定,然后再会取任务队列中拿任务2、3执行。打印结果如下:
10-10 17:51:36.074 16978-18462/com.cn.liuyz.threadpooldemo D/run:: 1
10-10 17:51:36.077 16978-18461/com.cn.liuyz.threadpooldemo D/run:: 0
10-10 17:51:36.078 16978-18463/com.cn.liuyz.threadpooldemo D/run:: 4
10-10 17:51:37.074 16978-18462/com.cn.liuyz.threadpooldemo D/run:: 2
10-10 17:51:37.078 16978-18461/com.cn.liuyz.threadpooldemo D/run:: 3
通过打印数据也印证了这一点
1、SingleThreadExecutor(单线程的线程池)
源码如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
从源码看:SingleThreadPool核心线程数时1,最大线程数也是1,LinkedBlockingQueue容量是Integer.MAX_VALUE,采用先进先出原则,所以这是一个单线程化的线程池,线程不会被回收,未处理的任务会放到任务队列中,保证了所有任务都在同一线程中顺序执行,因此不需要处理线程同步的问题。
使用:
private void btn2Click() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int temp = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
Log.d("run:", "" + temp);
}
};
executorService.execute(runnable);
}
}
输出结果为
10-10 17:59:33.235 24480-25562/com.cn.liuyz.threadpooldemo D/run:: 0
10-10 17:59:34.236 24480-25562/com.cn.liuyz.threadpooldemo D/run:: 1
10-10 17:59:35.237 24480-25562/com.cn.liuyz.threadpooldemo D/run:: 2
10-10 17:59:36.238 24480-25562/com.cn.liuyz.threadpooldemo D/run:: 3
10-10 17:59:37.238 24480-25562/com.cn.liuyz.threadpooldemo D/run:: 4
2、FixedThreadPool(定长线程池)
源码如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
从源码上看:FixedThreadPool的核心线程数和最大线程数相等,LinkedBlockingQueue容量也是Integer.MAX_VALUE,采用先进先出原则,所以所有线程也不会被回收,未处理的任务会放到任务队列中,所以FixedThreadPool是一个可创建定长的线程池,可控制最大并发数,当定长为1时和newSingleThreadExecutor一样。
使用:
private void btn3Click() {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
final int temp = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
Log.d("run:", "" + temp);
}
};
executorService.execute(runnable);
}
}
打印结果如下:
10-10 18:00:24.140 24480-26266/com.cn.liuyz.threadpooldemo D/run:: 2
10-10 18:00:24.143 24480-26265/com.cn.liuyz.threadpooldemo D/run:: 1
10-10 18:00:24.144 24480-26264/com.cn.liuyz.threadpooldemo D/run:: 0
10-10 18:00:25.141 24480-26266/com.cn.liuyz.threadpooldemo D/run:: 3
10-10 18:00:25.143 24480-26265/com.cn.liuyz.threadpooldemo D/run:: 4
3、CachedThreadPool(缓存线程池可全部回收)
源码如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
从源码看CachedThreadPool无核心线程,最大线程数为Integer.MAX_VALUE,保活时间为60S,当有新任务是,如果没有空闲线程则会创建新线程,因为任务队列为SynchronousQueue,所以最多有一个任务在队列中等到,也就是说CachedThreadPool可处理的最大任务数为Integer.MAX_VALUE+1。
使用:
private void btn4Click() {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
final int temp = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
Log.d("run:", "" + temp);
}
};
executorService.execute(runnable);
}
}
打印结果如下:
10-11 15:11:20.470 18498-18713/com.cn.liuyz.threadpooldemo D/run:: 2
10-11 15:11:20.472 18498-18712/com.cn.liuyz.threadpooldemo D/run:: 1
10-11 15:11:20.473 18498-18711/com.cn.liuyz.threadpooldemo D/run:: 0
10-11 15:11:20.474 18498-18714/com.cn.liuyz.threadpooldemo D/run:: 3
10-11 15:11:20.474 18498-18715/com.cn.liuyz.threadpooldemo D/run:: 4
4、ScheduledThreadPool(延迟或定时周期性执行任务)
源码如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
从源码看ScheduledThreadPool是一个定长的核心线程,Integer.MAX_VALUE最大线程数的线程池,主要用作延迟执行或定时周期性执行任务。
使用:
private void btn5Click(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
//延迟1s执行任务
executorService.schedule(new Runnable() {
@Override
public void run() {
Log.d("run:", "schedule-Runnable");
}
},1,TimeUnit.SECONDS);
//延迟2s执行任务
ScheduledFuture schedule = executorService.schedule(new Callable() {
@Override
public String call() throws Exception {
return "schedule-Callable";
}
}, 2, TimeUnit.SECONDS);
try {
String s = schedule.get();
Log.d("run:", s);
} catch (Exception e) {
Log.d("run:", "Exception");
}
//表示延迟3秒后每4秒执行一次。
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.d("run:" ,"scheduleAtFixedRate");
}
},3, 4, TimeUnit.SECONDS);
//表示延迟3秒后每5秒执行一次
executorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
Log.d("run:" ,"scheduleWithFixedDelay");
}
}, 3, 5, TimeUnit.SECONDS);
}
打印结果如下:
10-11 15:16:52.542 18498-23534/com.cn.liuyz.threadpooldemo D/run:: schedule-Runnable
10-11 15:16:53.543 18498-18498/com.cn.liuyz.threadpooldemo D/run:: schedule-Callable
10-11 15:16:56.544 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleAtFixedRate
10-11 15:16:56.544 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleWithFixedDelay
10-11 15:16:59.544 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleAtFixedRate
10-11 15:17:01.544 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleWithFixedDelay
10-11 15:17:02.544 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleAtFixedRate
10-11 15:17:05.544 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleAtFixedRate
10-11 15:17:06.545 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleWithFixedDelay
10-11 15:17:08.544 18498-23533/com.cn.liuyz.threadpooldemo D/run:: scheduleAtFixedRate
Runnable和Callable区别,scheduleAtFixedRate和scheduleWithFixedDelay区别另一篇文章再做详解
至此线程池的内容就介绍完毕了
Dmeo下载