简述Android Thread

目录:
[TOC]

线程

  • 什么是线程?

    • 线程是一种轻量级进程,大多数情况下用于执行异步操作。
    • 一个Android 程序开始运行的时候,会单独启动一个进程,同时会产生一个UI Thread线程。
    • 一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。
  • 线程与进程的区别

    1. 地址空间:
      • 线程是进程内的一个执行单元,一个进程至少有一个线程;
      • 线程共享进程的地址空间,而进程有自己独立的地址空间;
    2. 资源拥有:
      • 进程是资源分配和拥有的单位;
      • 同一个进程内的线程共享进程的资源;
    3. 线程是处理器调度的基本单位,但进程不是;
    4. 线程只需要很少的资源就可“轻装上阵”运行的优点,来弥补进程并发的“颗粒度”粗糙的缺点,提高系统资源利用率。
  • 为什么使用线程?

    • 在Android中线程分为主线程(UI线程)和子线程,主线程主要处理和界面相关的事情,而子线程则执行耗时操作。
    • 如果在主线程执行耗时操作时,如网络请求或数据库读取,就会阻塞主线程 其他逻辑的执行,导致程序无法及时响应从而导致界面卡顿。
    • 如果卡顿时间超过5秒,系统就会报ANR错误。所以,如果要执行耗时的操作,需要另起线程执行。
    • 注意:
      从Android 3.0 开始系统要求网络访问必须在子线程中进行,否则网络访问将失败并抛出NetworkOnMainThreadException异常,这样做是为了避免主线程由于耗时操作所阻塞从而出现ANR现象。
  • 线程的实现方法

    • 方法一:实现Thread

      • 扩展 java.lang.Thread 类

          Thread mThread=new Thread(new Runnable()    
                  {    
                      @Override    
                      public void run()    
                      {   
                          // method
                      }    
              });    
          mThread.start();
        
      • 实现 Runnable 接口

          public class ThreadTest extends Activity implements Runnable  
          {  
              @Override
              public void onCreate(Bundle savedInstanceState)
              {  
                  super.onCreate(savedInstanceState);  
                  setContentView(R.layout.main);  
          
                  Thread thread=new Thread(this);  
                  thread.start();  
              }  
        
              @Override  
              public void run()  
              {
                  // mothod 
              }  
          }
        
      • 继承 Thread 类与实现 Runnable 接口的区别

        • 在 Java API中,Thread是实现了Runnable接口;
        • 在 Java 中,类仅支持单继承,也就是说,当定义一个新的类的时候,它只能扩展一个外部类;
        • 使用实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享。
    • 方法三:实现AsyncTask
      AsyncTask是一种轻量级的异步任务类,它可在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
      AsyncTask封装了Thread和Handler,更加方便执行后台任务以及在主线程中访问UI,但不适合进行特别耗时的后台任务。
      AsyncTask是一个抽象的泛型类,提供了Params(参数类型)、Progess(后台任务的执行速度)和Result(后台任务的返回结果的类型)这三个泛型参数。

      • onPreExecute()
        开始执行前的准备工作;
      • doInBackground(Params...)
        开始执行后台处理,可以调用publishProgress方法来更新实时的任务进度;
      • onProgressUpdate(Progress...)
        在publishProgress方法被调用后,UI 线程将调用这个方法从而在界面上展示任务的进展情况;
      • onPostExecute(Result)
        执行完成后的操作,传送结果给UI线程。
      • onCancelled()
        在主线程中执行,任务被取消时调用,这个时候onPostExecute()则不会调用。
      • 注意:
        • AsyncTask的对象必须在UI线程中创建;
        • AsyncTask.execute()必须在UI线程中调用;
        • 除了doInBackground(Params...),其余方法都是被UI线程所调用的;
        • 一个AsyncTask对象只能被执行一次,即只能调用一次execute(),否则 将会出现运行时异常;
        • 这些方法都不能直接调用;
        • 不能手动停止。
        • Android版本区别
          • 在Android 1.6 之前,AsyncTask是串行执行任务的。
          • 在Android 1.6 的时候,AsyncTask采用线程池里处理并行任务。
          • 从Android 3.0 开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务。但,可以通过AsyncTask的executeOnExecutor()来并行地执行任务。
    • 方法四:实现IntentService
      IntentService继承了Service并且它是一个抽象类,IntentService封装了HandlerThread和Handler,用来处理异步请求,可执行后台耗时任务。
      IntentService通过worker thread处理每个Intent对象,执行完所有工作后自动停止Service。
      IntentService优先级比单纯的线程要高很多,所以适合执行高优先级的后台任务,因为不容易被系统杀死。

        public class IntentServiceTest extends IntentService {
            
            public IntentServiceTest() {   
                super("IntentServiceTest");   
            }  
            
            @Override
            public IBinder onBind(Intent intent)
            {
                return null;
            }
            
            @Override
            public void onCreate()
            {
                super.onCreate();
            }

            @Override
            public int onStartCommand(Intent intent, int flags, int startId)
            {
                return super.onStartCommand(intent, flags, startId);
            }
            
            @Override
            protected void onHandleIntent(Intent intent)
            {
                try {
                    //耗时操作
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            @Override
            public void onDestroy()
            {
                super.onDestroy();
            }
        }
        
        // 启动的IntentService
        Intent intent = new Intent(this, IntentServiceTest.class);   
        startService(intent); 
        startService(intent); 
        startService(intent); 
    
    
* 方法五:实现HandlerThread
    HandlerThread继承Thread,是一种可以使用Handler的Thread,通过run方法中Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。  
    由于HandlerThread的run方法是一个无限循环,在不使用时,需通过quit或者quitSafely方法来终止线程执行。
    
        public class HandlerThreadActivity extends Activity {  
            
            private static final int SIGN = 1;
        
            private HandlerThread mHandlerThread;
            private Handler mMsgHandler;
            private boolean isContinue;

            @Override
            protected void onCreate(Bundle savedInstanceState)
            {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_threadHandler);
        
                //创建后台线程
                initThread();
            }

            @Override
            protected void onResume()
            {
                super.onResume();
                //继续
                isContinue = true;
                mMsgHandler.sendEmptyMessage(SIGN);
            }

            @Override
            protected void onPause()
            {
                super.onPause();
                //停止继续
                isContinue = false;
                mMsgHandler.removeMessages(MSG_UPDATE_INFO);
            }

            private void initThread()
            {
                mHandlerThread = new HandlerThread("handlerThread");
                mHandlerThread.start();
                mMsgHandler = new Handler(mCheckMsgThread.getLooper())
                {
                    @Override
                    public void handleMessage(Message msg)
                    {
                        try {//模拟请求
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        
                        if (isContinue) {
                            mMsgHandler.sendEmptyMessageDelayed(SIGN, 1000);
                        }
                    }
                };
            }

            @Override
            protected void onDestroy()
            {
                super.onDestroy();
                //释放资源
                mHandlerThread.quit();
            }
        }

琐机制

Looper、Handler、MessageQueue、Message

  • Message
    消息体,封装了传输消息所需的数据结构。

  • Handler
    Message的主要处理者,负责Message的发送,Message内容的执行处理。

    • Handler会向message queue通过sendMessage或post两种方法发送消息

      • 都会插在message queue队尾并按先进先出执行
      • 通过sendMessage发送的是一个message对象,会被Handler的handleMessage()处理;
      • 通过post方法发送的是一个runnable对象,则会自己执行。
    • 使用Handler注意事项:

      • 创建massage对象时,推荐使用obtain()方法获取,因为Message内部会维护一个Message池用于Message的复用,这样就可以避免 重新new message而冲内心分配内存,减少new 对象产生的资源的消耗。
      • handler 的handleMessage方法内部如果有调用外部activity或者fragment的对象,一定要用弱饮用,handler最好定义成static的,这样可以避免内存泄漏;
        为什么呢?因为一但handler发送了消息。而handler内部有对外部变量的引用,此时handler已经进入了looper的messageQueue里面。此时activity或者fragment退出了可是区域,但是handler内部持有其引用且为强引用时,其就不会立即销毁,产生延迟销毁的情况;
      • 不确定当前线程时,更新UI时尽量调用post方法。
  • Lopper
    循环器,负责管理一个消息循环队列(MessageQueue)的。

  • MessageQueue
    消息队列,用来存放handler发布的消息,按照先进先出执行。

  • Thread、Lopper、Handler、MessageQueue 间关系

    • 一个 Thread 对应一个 Looper;

    • 一个 Looper 对应一个 MessageQueue;

    • 一个 Looper 可以对应多个 Handler;

    • 一个 Thread 可以有多个Handler;

    • 一个 Handler 只能和一个Thread 关联;

    • Handler 会引用当前线程里的特定 Looper 和 MessageQueue;

    • Handler 的处理过程运行在创建 Handler 的 Thread 里。

        class LooperThread extends Thread {   
      
            public Handler mHandler;   
           
            public void run() {   
                Looper.prepare(); //创建本线程的Looper并创建一个MessageQueue  
           
                mHandler = new Handler() {   
                    public void handleMessage(Message msg) {   
                        // process incoming messages here
                    }   
                };   
             
                Looper.loop(); //开始运行Looper,监听Message Queue   
            }   
        }
      

操作多线程的方式

  1. Handler + Thread
  2. AsyncTask
  3. ThreadPoolExecutor
  4. IntentService

子线程更新UI的方式

  1. View.post(Runnable action)
  2. Activity.runOnUiThread(Runnable action)
  3. AsyncTask
  4. Handler

线程池

  • 什么是线程池?
    线程池能够对线程进行简单管理,因为线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销。
    线程池中会缓存一定数量的线程,通过这个线程池就可以避免频繁创建和销毁线程带来的系统开销。

    • 优点
      • 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销;
      • 能够有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
  • ThreadPoolExecutor
    ThreadPoolExecutor提供一系列参数来配置线程池,通过不同参数可以创建不同的线程池。

    • 构造函数

      • int corePoolSize
        线程池维护线程的最少数量。
        默认情况下,核心线程数会在线程池中一直存活,即使处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为ture,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepLiveTime指定。当等待时间超过keepLiveTime,核心线程就会被终止。
      • int maximumPoolSize
        线程池维护线程的最大数量。
        活动线程达到这个数量,后续的新任务将会被阻塞。
      • long keepAliveTime
        线程池维护线程所允许的空闲时间,超过这个时长,线程就会被回收。
      • TimeUnit unit
        线程池维护线程所允许的空闲时间的单位,是一个枚举。
      • BlockingQueue<Runnable> workQueue
        线程池所使用的缓冲队列。
        通过线程池的execute方法提交的Runable对象会存储在这个参数中。
      • ThreadFactory threadFactory
        线程池用于创建线程。
        是一个接口,只有一个方法Thread newThread(Runnable r)。
      • RejectedExecutionHandler handler
        线程池对拒绝任务的处理策略。
        当线程池无法执行新任务时,可能是由于任务队列已满或者是无法成功执行任务,这个时候handler的rejectedExecution方法来通知调用者。
      • 配置参考(AsyncTask)
        • 核心线程数等于CPU核心数+1;
        • 最大线程数等于CPU核心数的2倍+1;
        • 核心线程无超时机制,非核心线程在闲置时的超时时间为1秒;
        • 任务队列的容量为128。
    • 线程创建规则
      ThreadPoolExecutor对象初始化时,不创建任何执行线程,当有新任务进来时,才会创建执行线程。

      • 当目前执行线程的总数小于核心线程大小时,所有新加入的任务,都在新线程中处理。
      • 当目前执行线程的总数大于或等于核心线程时,所有新加入的任务,都放入任务缓存队列中。
      • 当目前执行线程的总数大于或等于核心线程,并且缓存队列已满,同时此时线程总数小于线程池的最大大小,那么创建新线程,加入线程池中,协助处理新的任务。
      • 当所有线程都在执行,线程池大小已经达到上限,并且缓存队列已满时,就handler拒绝新的任务。
    • 线程池分类

      • FixedThreadPool
        通过Executors的newFixedThreadPool方法创建。
        线程数目固定且只有核心线程的线程池,线程处于空闲状态时,并不会被回收,除非线程池关闭。
        当所有线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。
        • 优点:只有核心线程并且不会被回收,能够更加快速的响应外界的请求。
      • CachedThreadPool
        通过Executors的newCachedThreadPool方法创建。
        线程数目不固定且只有非核心线程的线程池,并且最大线程数为Intager.MAX_VALUE。
        当所有线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。空闲线程有超时机制,超时时长为60秒,超过就会被回收。
        • 优点:适合执行大量的耗时较少的任务,当线程池处于闲置状态,线程都会超时停止,不占用系统资源。
      • ScheduledThreadPool
        通过Executors的newScheduledThreadPool方法创建。
        核心线程数固定,非核心线程数不固定的线程池,并且当非核心线程闲置时立刻回收。
        • 优点:用于执行定时任务和具有固定周期的重复任务。
      • SingleThreadExecutor
        通过Executors的newSingleThreadExecutor方法创建。
        只有一个核心线程的线程池,确保所有任务都在同一个线程中按顺序执行。
        • 优点:统一将外界任务到一个线程中,不需要处理线程同步问题。
    • 线程池执行

      • execute()中,调用了三个私有方法
      • addIfUnderCorePoolSize()
        在线程池大小小于核心线程池大小的情况下,扩展线程池
      • addIfUnderMaximumPoolSize()
        在线程池大小小于线程池大小上限的情况下,扩展线程池
      • ensureQueuedTaskHandled()
        保证在线程池关闭的情况下,新加入队列的线程也能正确处理
    • 线程池关闭

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