Andriod 线程间通信二:线程池

线程池

为什么要用线程池?

new Thread()的缺点

  • 每次new Thread()耗费性能
  • 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。
  • 不利于扩展,比如如定时执行、定期执行、线程中断

采用线程池的优点

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

#####线程池核心:ThreadPoolExcutor:

  • 核心线程数(coreThread)
  • 最大线程数(maxThread)
  • keepAliveTime,Unit(时间单位)
  • Queue(任务缓存队列)
  • threadFactory(线程创建工程)
  • RejectedExecutionHandler(拒绝执行handler)
  1. 当前正在执行的线程数 < 核心线程数的时候,新加入的任务就在新线程中执行
  2. 当前正在执行的线程数 > 核心线程数的时候,新加入的任务放入缓存队列
  3. 当前正在执行的线程数 >核心线程数的时候,缓存队列满了 且 当前正在执行的线程数<最大线程数,新建线程加入线程池
  4. 当前正在执行的线程数 >核心线程数的时候,缓存队列满了 且 当前正在执行的线程数=最大线程数,拒绝执行

一句话总结:先用核心线程,再用任务队列,再用”兼职“线程(最大 — 核心 ),最后拒绝执行

四种线程池(cfss)
  1. Excutors.newFixedThreadPool(5);

    1
    2
    3
    4
    5
    public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>());
    }

    总共只会创建5个线程, 开始执行五个线程,当五个线程都处于活动状态,再次提交的任务都会加入队列等到其他线程运行结束,当线程处于空闲状态时会被下一个任务复用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    ExecutorService es = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 50; i++) {
    Runnable runnable = new Runnable() {
    @Override
    public void run() {
    Log.d("kb_jay", Thread.currentThread().getName());
    }
    };
    es.execute(runnable);
    }

    /*
    08-14 22:26:43.546 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
    08-14 22:26:43.547 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
    08-14 22:26:43.547 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 22:26:43.548 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 22:26:43.548 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
    08-14 22:26:43.548 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    08-14 22:26:43.548 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
    08-14 22:26:43.548 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    pool-1-thread-2
    08-14 22:26:43.548 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
    08-14 22:26:43.548 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    08-14 22:26:43.548 18684-19118/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
    08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    08-14 22:26:43.549 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    08-14 22:26:43.549 18684-19118/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
    08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    08-14 22:26:43.549 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
    08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    08-14 22:26:43.549 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
    08-14 22:26:43.549 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
    08-14 22:26:43.550 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
    08-14 22:26:43.550 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    */
  2. Excutors.newCachedThreadPool();

    缓存线程池大小是不定值,可以需要创建不同数量的线程,在使用缓存型池时,先查看池中有没有以前创建的线程,如果有,就复用.如果没有,就新建新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务

    1
    2
    3
    4
    5
    6
    public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    60L, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>());
    }
    //空闲线程在在60s内不会被回收

    执行如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    ExecutorService es = Executors.newCachedThreadPool();
    for (int i = 0; i < 50; i++) {
    Runnable runnable = new Runnable() {
    @Override
    public void run() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    Log.d("kb_jay", Thread.currentThread().getName());
    }
    };
    es.execute(runnable);
    }
    /*
    08-14 22:41:57.302 21468-21584/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
    08-14 22:41:57.302 21468-21582/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 22:41:57.302 21468-21585/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
    08-14 22:41:57.303 21468-21586/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
    08-14 22:41:57.303 21468-21587/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
    08-14 22:41:57.304 21468-21590/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-6
    08-14 22:41:57.304 21468-21591/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-7
    08-14 22:41:57.305 21468-21592/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-8
    08-14 22:41:57.305 21468-21593/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-9
    08-14 22:41:57.306 21468-21594/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-10
    08-14 22:41:57.308 21468-21596/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-11
    08-14 22:41:57.309 21468-21597/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-12
    08-14 22:41:57.309 21468-21598/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-13
    08-14 22:41:57.312 21468-21599/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-14
    08-14 22:41:57.312 21468-21600/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-15
    08-14 22:41:57.314 21468-21601/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-16
    08-14 22:41:57.315 21468-21603/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-18
    。。。
    */

    当线程sleep 1s时,几乎每个runnable都会创建一个新的线程,这是因为每个runnable开始执行时都没有cache线程(空闲时间没有超过60s的线程),所以自己要new一个线程出来。

    每个runnable的生存期长,不适合。

去掉sleep 1s后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void start2() {
ExecutorService es = Executors.newCachedThreadPool();
for (int i = 0; i < 50; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.d("kb_jay", Thread.currentThread().getName());
}
};
es.execute(runnable);
}
}

/*
08-14 22:52:19.609 22498-22655/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 22:52:19.610 22498-22656/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
pool-1-thread-2
08-14 22:52:19.610 22498-22655/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 22:52:19.610 22498-22656/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:52:19.610 22498-22657/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
08-14 22:52:19.611 22498-22657/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
08-14 22:52:19.611 22498-22658/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:52:19.611 22498-22656/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:52:19.612 22498-22655/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 22:52:19.612 22498-22656/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:52:19.613 22498-22658/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:52:19.613 22498-22659/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
08-14 22:52:19.613 22498-22658/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:52:19.613 22498-22656/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:52:19.613 22498-22658/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:52:19.613 22498-22659/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
。。。
*/

会发现只创建了少量的线程,会有线程的复用情况。这是因为部分runnale开始执行时线程池中有空闲线程且该线程空闲时间没有超过60s,可以复用。

每一个runnable的生存期短,适合。

  1. Excutors.newScheduleThreadPool(5);

    这个跟fixedThreadPool相比,只是多了个延迟开始执行的功能

    执行如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    ScheduledExecutorService es = Executors.newScheduledThreadPool(5);
    for (int i = 0; i < 50; i++) {
    Runnable runnable = new Runnable() {
    @Override
    public void run() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    Log.d("kb_jay", Thread.currentThread().getName());
    }
    };
    es.schedule(runnable,5000, TimeUnit.MILLISECONDS);
    }

    /*
    08-14 23:06:03.560 24100-24176/? D/kb_jay: pool-1-thread-1
    08-14 23:06:03.560 24100-24177/? D/kb_jay: pool-1-thread-2
    08-14 23:06:03.561 24100-24178/? D/kb_jay: pool-1-thread-3
    08-14 23:06:03.562 24100-24179/? D/kb_jay: pool-1-thread-4
    08-14 23:06:03.562 24100-24180/? D/kb_jay: pool-1-thread-5
    08-14 23:06:04.562 24100-24177/? D/kb_jay: pool-1-thread-2
    08-14 23:06:04.562 24100-24176/? D/kb_jay: pool-1-thread-1
    08-14 23:06:04.562 24100-24178/? D/kb_jay: pool-1-thread-3
    08-14 23:06:04.563 24100-24179/? D/kb_jay: pool-1-thread-4
    08-14 23:06:04.563 24100-24180/? D/kb_jay: pool-1-thread-5
    08-14 23:06:05.563 24100-24178/? D/kb_jay: pool-1-thread-3
    08-14 23:06:05.563 24100-24177/? D/kb_jay: pool-1-thread-2
    08-14 23:06:05.564 24100-24180/? D/kb_jay: pool-1-thread-5
    08-14 23:06:05.564 24100-24176/? D/kb_jay: pool-1-thread-1
    。。。
    */

    schedule(Runnable command,long delay, TimeUnit unit)

    创建并执行在给定延迟后启用的一次性操作

**scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnitunit)**  

创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推

  1. Excutors.newSingleThreadExcutor();

    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    1
    2
    3
    4
    5
    6
    public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>()));
    }

    执行如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    ExecutorService es = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 50; i++) {
    Runnable runnable = new Runnable() {
    @Override
    public void run() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    Log.d("kb_jay", Thread.currentThread().getName());
    }
    };
    es.execute(runnable);
    }

    /*
    08-14 23:20:28.503 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 23:20:29.504 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 23:20:30.506 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 23:20:31.507 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 23:20:32.509 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    08-14 23:20:33.511 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
    。。。
    */

####

####