美文网首页
Android 线程切换,线程池。

Android 线程切换,线程池。

作者: 844b9a3a3a68 | 来源:发表于2017-05-22 12:02 被阅读267次

在开发中,我们往往需要用到线程切换这个功能,最常见的使用场景就是,当我们需要做网络请求或者其他耗时处理时,不能在主线程调用,否则容易造成线程堵塞,很容易触发ANR,所以我们一般在这时候都会新建一个子线程来执行:

        new Thread(new Runnable() {
            @Override
            public void run() {
                //网络加载图片的方法
                loadImage();
            }
        }).start();

然而这时候问题又来了,当图片下载完成,我们为ImageView设置背景的时候,却又抛出了异常,为什么呢?这是由于Android机制中,不允许在非UI线程更新UI,通俗的讲就是不能在子线程更新UI。Android为我们暴露了一个方法,这个方法是在主线程执行,例:

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                image.setImageBitmap(bitmap);
            }
        });

当然,Android还为我们提供了一个Handler类,内部基于线程池,使用更加灵活和方便,官方也更推荐我们使用Handler来更新UI。

        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
            image.setImageBitmap(bitmap);
            }
        });

至此,Android中的线程切换就是这样了,下面我们看看这样做有什么弊端:

new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

所以在有些特定场景,我们需要用到线程池:ExecutorService

 Java通过Executors提供四种线程池,分别为: 
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可收,则新建线程。 
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
        ExecutorService service= Executors.newCachedThreadPool();
        service.submit(new Runnable() {
            @Override
            public void run() {
                //网络加载图片的方法
                loadImage();
            }
        });

(1). newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
for (int i = 0; i < 10; i++) {  
    final int index = i;  
    try {  
        Thread.sleep(index * 1000);  
    } catch (InterruptedException e) {  
        e.printStackTrace();  
    }  
  
    cachedThreadPool.execute(new Runnable() {  
  
        @Override  
        public void run() {  
            System.out.println(index);  
        }  
    });  
} 

(2). newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
for (int i = 0; i < 10; i++) {  
    final int index = i;  
    fixedThreadPool.execute(new Runnable() {  
  
  
        @Override  
        public void run() {  
            try {  
                System.out.println(index);  
                Thread.sleep(2000);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    });  
} 
因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。可参考PreloadDataCache。

(3) newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
scheduledThreadPool.schedule(new Runnable() {  
  
    @Override  
    public void run() {  
        System.out.println("delay 3 seconds");  
    }  
}, 3, TimeUnit.SECONDS);  //表示延迟3秒执行。

定期执行示例代码如下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  
  
    @Override  
    public void run() {  
        System.out.println("delay 1 seconds, and excute every 3 seconds");  
    }  
}, 1, 3, TimeUnit.SECONDS); 
表示延迟1秒后每3秒执行一次。
ScheduledExecutorService比Timer更安全,功能更强大。

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

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  
for (int i = 0; i < 10; i++) {  
    final int index = i;  
    singleThreadExecutor.execute(new Runnable() {  
  
        @Override  
        public void run() {  
            try {  
                System.out.println(index);  
                Thread.sleep(2000);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    });  
} 
结果依次输出,相当于顺序执行各个任务。
现行大多数GUI程序都是单线程的。Android中单线程可用于数据库操作,文件操作,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操作。

ExecutorService 的submit() 与execute()区别

1.接收的参数不一样 submit()可以接受runnable无返回值和callable有返回值 execute()接受runnable 无返回值

2.submit有返回值,而execute没有
    2.1用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。
    
3.submit方便Exception处理
    3.1意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。

shotdown() showdownNow()区别

可以关闭 ExecutorService,这将导致其拒绝新任务。提供两个方法来关闭 ExecutorService。 shutdown() 方法在终止前允许执行以前提交的任务, shutdownNow() 方法阻止等待任务启动并试图停止当前正执行的任务。在终止时执行程序没有任务在执行,也没有任务在等待执行,并且无法提交新任务。关闭未使用的 ExecutorService 以允许回收其资源。 一般分两个阶段关闭 ExecutorService。第一阶段调用 shutdown 拒传入任务,然后调用 shutdownNow(如有必要)取消所有遗留的任务
// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
    threadPool.shutdown();

相关文章

  • Android 线程切换,线程池。

    在开发中,我们往往需要用到线程切换这个功能,最常见的使用场景就是,当我们需要做网络请求或者其他耗时处理时,不能在主...

  • 线程池创建和相关知识

    线程池创建(单例):Android线程池得要这么用 - 简书 线程池相关知识:Android开发之线程池使用总结 ...

  • Android线程池的使用

    一、线程与线程池,为什么要使用线程池 1、Android中的线程 在Android中有主线程和子线程的区分。主线程...

  • 线程池

    话题:线程池Android中的线程池有哪些?它们的区别是什么?为什么要使用线程池? 线程是Android里面一个很...

  • Android的线程和线程池

    Android的线程和线程池 标签(空格分隔): android 一些概念: 线程分类:主线程和子线程,主线程主要...

  • 笔记:Android线程和线程池

    Android线程和线程池 Android中的线程操作相关的类有 AsyncTask IntentService ...

  • Android 面试系统复习系列(四)线程 / 线程池 / 多线

    Android 面试系统复习系列(四)线程 / 线程池 / 多线程 线程 线程的状态 New ...

  • 第十九周 线程池

    话题:线程池 Android 中的线程池有哪些?它们的区别是什么?为什么要使用线程池?关键字:线程池、Thread...

  • Android中的线程池

    为什么要使用线程池?Android 中的线程池有哪些?它们的区别是什么? 一、线程池的作用 重用线程池中的线程,可...

  • Android面试之线程和线程池

    Android中的线程形态 AsyncTask底层用到了线程池。AsyncTask封装了线程池和Handler,它...

网友评论

      本文标题:Android 线程切换,线程池。

      本文链接:https://www.haomeiwen.com/subject/vypbxxtx.html