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

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

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

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

  1. 如果某个地方需要开启大量的线程。创建线程和销毁线程是需要时间的,这样会导致性能不足
  2. 线程无法管理,相互竞争导致卡顿或者oom
  3. 功能太过单一

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

线程池: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 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、付费专栏及课程。

余额充值