android线程及线程池

众所周知,在UI系统中进行一些耗时操作,都会导致卡顿现象,因为一次刷新在16ms,如果当次操作过了这个时间,那么用户就能感觉到明显的卡顿,甚至引起ANR 。
对于这种情况,一般都是再起一个线程,进行一些耗时的操作,通过继承Thread 或者实现Runnable接口,重写run方法,来实现创建新线程,进行耗时操作的需求.由于java的单继承关系,首推实现Runnable方法,这样不会太过臃肿。

private void runThread() {
        Thread t1 = new Thread(new MyRunnable(this), "郭富城");
        t1.start();
      //强调一点,调用run方法是真的调用方法,而调用start方法则会创建新线程去调用这个run方法。
    }

//实现runnable方法  至于继承Thread方法的代码 ,就不写了
    static class MyRunnable implements Runnable {
        private WeakReference<MainActivity> reference;

        MyRunnable(MainActivity activity) {
            reference = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void run() {
            if (reference != null) {
                MainActivity activity = reference.get();
                if (activity != null) {
                    //子线程不能弹toast的 所以加了点东西
                    Looper.prepare();
                    Toast.makeText(activity, Thread.currentThread().getName() + " is Runing", Toast.LENGTH_SHORT).show();
                    Looper.loop();
                }
            }
        }
    }

另外,jdk1.5版本以后,就新加了个Callable接口,相较于Runnable接口,他可以定义返回值,并且可以抛出异常,需要实现的方法也从run方法变成了call方法。还有一点很有意思,他返回一个Future对象,通过这个对象,我们可以知道任务的进行程序,并且可以关闭这个任务。Thread是不支持这个接口的 ,所以如果要用Callable,就需要用上FutureTask.

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

简单的说,FutureTask是一种可以取消的异步任务,异步执行任务, 可以开始、取消以及查看任务是否完成, 如果任务没有执行完,get方法会导致线程阻塞, 一旦一个执行任务已经完成就不能再次开始和结束(除非执行时通过runAndReset()方法.另外,FutureTask也同样支持Runnable接口。

 private void runFutureTask() {
        //创建线程池 不用线程池 直接用future也可以的
        final ExecutorService exec = Executors.newFixedThreadPool(5);
        //创建futureTask任务
        final FutureTask<String> ft = new FutureTask<String>(new MyCallable());
        // 执行也可以 提交也可以 还有种invokeAll方法
        exec.execute(ft);
//        exec.submit(ft);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //get方法会自动阻塞当前线程  直到拿到结果
                    //所以不要放在主线程去get 不然就卡住了 虽然我试过卡个15秒也没事
                    final String text = ft.get();
                    Looper.prepare();
                    Toast.makeText(FutureTaskActivity.this, text, Toast.LENGTH_SHORT).show();
                    Looper.loop();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //用完线程池 记得关闭掉
        exec.shutdown();
    }

    static class MyCallable implements Callable<String> {

        @Override
        public String call() throws Exception {
            Thread.sleep(3000);
            return "代码让我快乐,代码让我秃顶";
        }
    }

线程是CPU调度的最小单元,对于线程的创建,销毁,调度,其实也是在耗费一定资源的,特别是某些情况下,你需要创建一堆线程进行一些简单的耗时操作时,这个资源的消耗量还是非常大的,所以就有线程池的需求了。

我们来总结一下优点吧。
1.重用线程池中的线程,避免频繁地创建和销毁线程带来的性能消耗;
2.有效控制线程的最大并发数量,防止线程过大导致抢占资源造成系统阻塞;
3.可以对线程进行一定地管理。

使用线程池 相对于每次都new 线程,性能会好很多,并且可以统一管理,功能上也能有所改善,如果需要定时执行,定期执行,线程中断等。
减少内存开销。

ThreadPoolExecutor:
  ExecutorService是最初的线程池接口,ThreadPoolExecutor类是对线程池的具体实现,它通过构造方法来配置线程池的参数,我们来分析一下它常用的构造函数吧。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

参数解释:

    corePoolSize,线程池中核心线程的数量,默认情况下,即使核心线程没有任务在执行它也存在的,我们固定一定数量的核心线程且它一直存活这样就避免了一般情况下CPU创建和销毁线程带来的开销。我们如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程就会有超时策略,这个时间由keepAliveTime来设定,即keepAliveTime时间内如果核心线程没有回应则该线程就会被终止。allowCoreThreadTimeOut默认为false,核心线程没有超时时间。
    maximumPoolSize,线程池中的最大线程数,当任务数量超过最大线程数时其它任务可能就会被阻塞。最大线程数=核心线程+非核心线程。非核心线程只有当核心线程不够用且线程池有空余时才会被创建,执行完任务后非核心线程会被销毁。
    keepAliveTime,非核心线程的超时时长,当执行时间超过这个时间时,非核心线程就会被回收。当allowCoreThreadTimeOut设置为true时,此属性也作用在核心线程上。
    unit,枚举时间单位,TimeUnit。
    workQueue,线程池中的任务队列,我们提交给线程池的runnable会被存储在这个对象上。

线程池的分配遵循这样的规则:

    当线程池中的核心线程数量未达到最大线程数时,启动一个核心线程去执行任务;
    如果线程池中的核心线程数量达到最大线程数时,那么任务会被插入到任务队列中排队等待执行;
    如果在上一步骤中任务队列已满但是线程池中线程数量未达到限定线程总数,那么启动一个非核心线程来处理任务;
    如果上一步骤中线程数量达到了限定线程总量,那么线程池则拒绝执行该任务,且ThreadPoolExecutor会调用RejectedtionHandler的rejectedExecution方法来通知调用者。

四种线程池 都可以通过Executors提供:

  1. newCachedThreadPool:创建一个可缓存线程池,他是一个不限容量的线程池,其中创建的所有的线程都是非核心线程,如果线程池长度超过处理需要,超时时间设置为60s,如果当前没有任务,会回收空闲线程,若无可回收,则新建线程。用于执行一些生存期很短的异步型任务
public static ExecutorService new CachedThreadPool(){
    return new ThreadPoolExecutor(
        0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>()
    );
}
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  1. newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。池线程数固定,创建的线程会一直存在。它没有超时机制并且等待队列无限常,所以fixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器。
public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(
        nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>() 
        );
}
//创建线程池
ExecutorService mExecutor = Executors.newFixedThreadPool(5);//参数即核心线程数
  1. newScheduledThreadPool 创建一个定长线程池,其实他将上面两种线程池的优点全部集结成一块,他有一定数量的核心线程,并且无限容量的非核心线程,区别在于非核心线程的超时时间设定为0秒,即一旦空闲,立马回收,支持定时及周期性任务执行。
public static ScheduledThreadPool newScheduledThreadPool(int corePoolSize){
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);//核心线程数
  1. newSingleThreadExecutor 创建一个单线程化的线程池,他只有一个核心线程,它也只会用唯一的工作线程来执行任务,可以让调用者忽略线程同步的问题。
public static ExecutorService newSingleThreadExecutor(){
    return new FinalizableDelegatedExecutorService(
        new ThreadPoolExecutor(
        1, 1, 0L, TimeUnit.MILLISECONDS, 
        new LinkedBlockingQueue<Runnable>()));
}
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

线程池一般用法

    shutDown(),关闭线程池,需要执行完已提交的任务;
    shutDownNow(),关闭线程池,并尝试结束已提交的任务;
    allowCoreThreadTimeOut(boolen),允许核心线程闲置超时回收;
    execute(),提交任务无返回值;
    submit(),提交任务有返回值;

newCachedThreadPool缓存线程池,如果线程超出则灵活回收,如果不够则创建
newFixedThreadPool 定长线程池,可以设置最大线程数,超出的任务在队列中等候。
newScheduledThreadPool 也是定长线程池 ,支持定时及周期性任务。
newSingleThreadExecutor 单线程化的线程池,任务根据加入顺序在队列中慢慢等待执行。

除了以上这些,在Android中充当线程的角色还有AsyncTask、HandlerThread、IntentService。它们本质上都是由Handler+Thread来构成的。

AsyncTask,它封装了线程池和Handler,主要为我们在子线程中更新UI提供便利。
HandlerThread,它是个具有消息队列的线程,可以方便我们在子线程中处理不同的事务。
IntentService,我们可以将它看做为HandlerThread的升级版,它是服务,优先级更高。

参考
http://blog.csdn.net/weixin_36244867/article/details/72832632
http://blog.csdn.net/linchunquan/article/details/22382487
http://www.hchstudio.cn/2017/04/01/FutureTask%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/
http://www.cnblogs.com/wenjiang/archive/2012/09/02/2668089.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 前段时间遇到这样一个问题,有人问微信朋友圈的上传图片的功能怎么做才能让用户的等待时间较短,比如说一下上传9张图片,...
    加油码农阅读 1,190评论 0 2
  • 原文链接:http://blog.csdn.net/u010687392/article/details/4985...
    xpengb阅读 1,316评论 0 1
  • 目前的工作是接触大数据相关的内容,自己也缺少高并发的知识,刚好前几天看了flume的源码,里面也用到了各种线程池内...
    等一夏_81f7阅读 1,394评论 0 0
  • 先看几个概念:线程:进程中负责程序执行的执行单元。一个进程中至少有一个线程。多线程:解决多任务同时执行的需求,合理...
    yeying12321阅读 538评论 0 0
  • 中午突然想做愤怒静心!又不知道该表达什么!于是就一直说:我很生气,我很生气……同时用棍子敲打枕头! 然后开始表达对...
    竺子阅读 98评论 0 0