Android 线程池的使用

一:使用线程池的原因

在android开发中经常会使用多线程异步来处理相关任务,而如果用传统的newThread来创建一个子线程进行处理,会造成一些严重的问题:

1:在任务众多的情况下,系统要为每一个任务创建一个线程,而任务执行完毕后会销毁每一个线程,所以会造成线程频繁地创建与销毁。

2:多个线程频繁地创建会占用大量的资源,并且在资源竞争的时候就容易出现问题,同时这么多的线程缺乏一个统一的管理,容易造成界面的卡顿。

3:多个线程频繁地销毁,会频繁地调用GC机制,这会使性能降低,又非常耗时。

总而言之:频繁地为每一个任务创建一个线程,缺乏统一管理,降低性能,并且容易出现问题。

为了解决这些问题,就要用到今天的主角——线程池.

线程池使用的好处:

1:对多个线程进行统一地管理,避免资源竞争中出现的问题。

2:(重点):对线程进行复用,线程在执行完任务后不会立刻销毁,而会等待另外的任务,这样就不会频繁地创建、销毁线程和调用GC。

3:JAVA提供了一套完整的ExecutorService线程池创建的api,可创建多种功能不一的线程池,使用起来很方便。

二:几种常见的线程池

1:ThreadPoolExecutor 创建基本线程池

创建线程池,主要是利用ThreadPoolExecutor这个类,而这个类有几种构造方法,其中参数最多的一种构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        ...
}

corePoolSize: 该线程池中核心线程的数量。

maximumPoolSize:该线程池中最大线程数量。(区别于corePoolSize)

keepAliveTime:从字面上就可以理解,是非核心线程空闲时要等待下一个任务到来的时间,当任务很多,每个任务执行时间很短的情况下调大该值有助于提高线程利用率。注意:当allowCoreThreadTimeOut属性设为true时,该属性也可用于核心线程。

unit:前一个参数时间属性的单位

workQueue:任务队列,后面详述。

threadFactory:线程工厂,可用于设置线程名字等等,一般无须设置该参数。

设置好几个参数就可以创建一个基本的线程池,而之后的各种线程池都是在这种基本线程池的基础上延伸的。

下面看个demo:点击按钮一次启动30个线程,每个线程都是2秒以后打印出自己的线程名和当前是第几个起的线程

 ThreadPoolExecutor threadPoolExecutor;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        threadPoolExecutor = new ThreadPoolExecutor(3,5,1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(100));
    }

    public void onClickListener(View v) {
        for(int i = 0;i<30;i++){
            final int finali = i;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                        Log.d("当前线程:",Thread.currentThread().getName() + "-------------" + finali);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            threadPoolExecutor.execute(runnable);
        }
    }
2019-05-31 17:22:23.222 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------1
2019-05-31 17:22:23.222 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------0
2019-05-31 17:22:23.222 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------2
2019-05-31 17:22:25.223 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------3
2019-05-31 17:22:25.224 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------4
2019-05-31 17:22:25.224 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------5
2019-05-31 17:22:27.225 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------7
2019-05-31 17:22:27.225 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------6
2019-05-31 17:22:27.225 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------8
2019-05-31 17:22:29.227 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------10
2019-05-31 17:22:29.227 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------11
2019-05-31 17:22:29.227 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------9
2019-05-31 17:22:29.227 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------11
2019-05-31 17:22:31.228 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------13
2019-05-31 17:22:31.228 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------12
2019-05-31 17:22:31.228 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------13
2019-05-31 17:22:31.229 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------14
2019-05-31 17:22:33.230 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------15
2019-05-31 17:22:33.230 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------16
2019-05-31 17:22:33.231 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------17
2019-05-31 17:22:35.231 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------18
2019-05-31 17:22:35.231 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------19
2019-05-31 17:22:35.231 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------18
2019-05-31 17:22:35.232 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------20
2019-05-31 17:22:37.233 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------22
2019-05-31 17:22:37.233 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------21
2019-05-31 17:22:37.234 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------23
2019-05-31 17:22:39.235 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------25
2019-05-31 17:22:39.235 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------24
2019-05-31 17:22:39.235 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------26
2019-05-31 17:22:41.236 5418-6011/com.series.myapplication D/当前线程:: pool-1-thread-1-------------28
2019-05-31 17:22:41.236 5418-6012/com.series.myapplication D/当前线程:: pool-1-thread-2-------------27
2019-05-31 17:22:41.237 5418-6013/com.series.myapplication D/当前线程:: pool-1-thread-3-------------29

上面线程3个3个执行,每两秒打印3个, 因为只有3个线程在工作,所以要分10次打印。

具体过程:

(1).execute一个线程之后,如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。

(2).execute一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue未满,则将新线程放入workQueue中等待执行。

(3).execute一个线程之后,如果线程池中的线程数已经达到核心线程数但未超过非核心线程数,且workQueue已满,则开启一个非核心线程来执行任务。

(4).execute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则拒绝执行该任务,采取饱和策略,并抛出RejectedExecutionException异常。

2、FixedThreadPool (可重用固定线程数):

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

特点:参数为核心线程数,只有核心线程,无非核心线程,从参数看核心线程数和最大线程数都是nThreads,并且阻塞队列无界。

3:CachedThreadPool (按需创建)

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

从参数上看,该线程池没有核心线程,只有非核心线程,而且是无限大,每个线程空闲后等待60秒被销毁,采用SynchronousQueue队列,还是上面的demo,采用CachedThreadPool会一次打印30条

  • 因为没有核心线程,其他全为非核心线程,SynchronousQueue是不存储元素的,每次插入操作必须伴随一个移除操作,一个移除操作也要伴随一个插入操作。
  • 当一个任务执行时,先用SynchronousQueue的offer提交任务,如果线程池中有线程空闲,则调用SynchronousQueue的poll方法来移除任务并交给线程处理;如果没有线程空闲,则开启一个新的非核心线程来处理任务。
  • 由于maximumPoolSize是无界的,所以如果线程处理任务速度小于提交任务的速度,则会不断地创建新的线程,这时需要注意不要过度创建,应采取措施调整双方速度,不然线程创建太多会影响性能。
  • 从其特点可以看出,CachedThreadPool适用于有大量需要立即执行的耗时少的任务的情况。

4:SingleThreadPool(单个核线的fixed)

//创建Single线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

从参数看,核心线程是1,最大线程也是1, 只有一个核心线程,用的是阻塞队列, 只有一个线程任务在跑,其他全部阻塞

5、ScheduledThreadPool(定时延时执行)

//创建Scheduled线程池
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);

使用: 线程3秒后执行

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        Log.d("当前线程:",Thread.currentThread().getName());
    }        
};
scheduledThreadPool.schedule(runnable, 3, TimeUnit.SECONDS);

6、自定义的PriorityThreadPool(队列中有优先级比较的线程池)

//创建自定义线程池(优先级线程),3个核心线程,线程最大数为3, 非核心线程空闲等待为0秒
ExecutorService priorityThreadPool = new ThreadPoolExecutor(3,3,0, TimeUnit.SECONDS,new PriorityBlockingQueue<Runnable>());

// Runnable要集成Comparable接口
public abstract class PriorityRunnable implements Runnable,Comparable<PriorityRunnable> {
    private int priority;

    public  PriorityRunnable(int priority){
        if(priority <0) {
            throw new IllegalArgumentException();
        }
        this.priority = priority;
    }

    public int getPriority() {
        return priority;
    }

    @Override
    public int compareTo(@NonNull PriorityRunnable another) {
        int me = this.priority;
        int anotherPri=another.getPriority();
        return me == anotherPri ? 0 : me < anotherPri ? 1 : -1;
    }

    @Override
    public void run() {
            doSomeThing();
    }

    protected abstract void doSomeThing();
}

使用:

public void onClickListener(View v) {
        for(int i = 0;i<10;i++){
            final int priority = i+1;
            priorityThreadPool.execute(new MyRunnable(priority) {
                @Override
                protected void doSomeThing() {
                    Log.d("dongjiejie", "优先级为 "+priority+"  的任务被执行");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
}

输出结果:

2019-06-03 09:43:09.059 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 1  的任务被执行
2019-06-03 09:43:11.061 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 10  的任务被执行
2019-06-03 09:43:13.062 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 9  的任务被执行
2019-06-03 09:43:15.063 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 8  的任务被执行
2019-06-03 09:43:17.063 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 7  的任务被执行
2019-06-03 09:43:19.065 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 6  的任务被执行
2019-06-03 09:43:21.066 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 5  的任务被执行
2019-06-03 09:43:23.068 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 4  的任务被执行
2019-06-03 09:43:25.069 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 3  的任务被执行
2019-06-03 09:43:27.070 18374-18536/com.series.myapplication D/dongjiejie: 优先级为 2  的任务被执行

因为是循环,所以第一个线程没有人与他相比较,所以肯定会优先执行,然后剩下的线程按优先级一次执行

三、阻塞队列,Java中常见的阻塞队列有这七种

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

四、常用的其他线程池方法

1.shutDown()  关闭线程池,不影响已经提交的任务

2.shutDownNow() 关闭线程池,并尝试去终止正在执行的线程

3.allowCoreThreadTimeOut(boolean value) 允许核心线程闲置超时时被回收

4.submit 一般情况下我们使用execute来提交任务,但是有时候可能也会用到submit,使用submit的好处是submit有返回值。

5.beforeExecute() - 任务执行前执行的方法

6.afterExecute() -任务执行结束后执行的方法

7.terminated() -线程池关闭后执行的方法

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Studio 中使用线程池需要以下步骤: 1. 创建一个线程池对象。可以使用 Executors 类中的静态方法来创建线程池对象,例如:`ExecutorService executor = Executors.newFixedThreadPool(3);`,这里创建了一个固定大小为3的线程池。 2. 创建一个实现 Runnable 接口的类,重写 run() 方法,将需要执行的操作放在 run() 方法中。 3. 将 Runnable 对象提交给线程池进行执行,可以使用 execute() 方法或 submit() 方法,例如:`executor.execute(new MyRunnable());`,这里将一个 MyRunnable 对象提交给线程池进行执行。 4. 当不需要使用线程池时,需要调用 shutdown() 方法关闭线程池。例如:`executor.shutdown();` 下面是一个完整的示例代码: ``` public class MainActivity extends AppCompatActivity { private ExecutorService executor; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text_view); // 创建线程池对象 executor = Executors.newFixedThreadPool(3); // 提交任务 executor.execute(new MyRunnable()); executor.execute(new MyRunnable()); executor.execute(new MyRunnable()); } @Override protected void onDestroy() { super.onDestroy(); // 关闭线程池 executor.shutdown(); } private class MyRunnable implements Runnable { @Override public void run() { // 在这里执行需要执行的操作 // 可以更新 UI,但需要使用 runOnUiThread() 方法 runOnUiThread(new Runnable() { @Override public void run() { textView.setText("Hello World"); } }); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值