<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android 多線程并發(fā)優(yōu)化實(shí)現(xiàn)

          共 8951字,需瀏覽 18分鐘

           ·

          2024-04-10 22:57

          cd2c475b73344ae10ca8720147502c29.webp

          和你一起終身學(xué) 習(xí),這里是程序員Android

          經(jīng)典好文推薦,通過閱讀本文,您將收獲以下知識(shí)點(diǎn):

          一、Thread 使用
          二、Android Thread
          三.線程優(yōu)先級(jí)

          一、Thread 使用

          在講解多線程之前,我們先來講解Thread使用幾個(gè)需要注意的點(diǎn):

          1.Thread 中斷

          常用的有兩種方式:

          (1).通過拋出InterruptedException來中斷線程

                  public  static  class  MyThread extends Thread{
          private int count=0;
          @Override
          public void run() {
          super.run();
          try{
          while(true){
          count++;
          System.out.println("count value:"+count);
          if (this.interrupted() || this.isInterrupted()){
          System.out.println("check interrupted show!");
          throw new InterruptedException();
          }
          }
          }catch ( InterruptedException e) {
          System.out.println("thread is stop!");
          e.printStackTrace();
          }
          }

          }

          (2).通過變量來中斷(常用)

                  public  static  class  CustomThread extends Thread{
          private int count=0;
          private boolean isCancel = false;
          @Override
          public void run() {
          super.run();
          while(!isCancel){
          count++;
          System.out.println("count value:"+count);
          }
          }

          public synchronized void cancel(){
          isCancel = true;
          }
          }

          2.Thread 同步

          我們分變量同步和代碼塊同步兩個(gè)方面來講解

          (1).變量同步

          • 使用volatile關(guān)鍵字

                  /**
          * 主內(nèi)存和線程內(nèi)存緩存進(jìn)行同步
          */

          volatile int val = 5;
          public int getVal() {
          return val;
          }
          public void setVal(int val) {
          this.val = val;
          }

          • 使用synchronized關(guān)鍵字

                  int val2 = 5;
          /**
          * 使用一個(gè)motinor來監(jiān)聽(實(shí)現(xiàn)資源由一個(gè)線程進(jìn)行操作)
          * 主內(nèi)存和線程內(nèi)存緩存進(jìn)行同步
          * @return
          */

          public synchronized int getVal2() {
          return val2;
          }
          public synchronized int setVal2(int val) {
          this.val2 = val;
          }

          • 使用關(guān)鍵字AtomicXXXXX

                  AtomicInteger mAtomicValue = new  AtomicInteger(0);
          public void setAtomicValue(int value){
          mAtomicValue.getAndSet(value);
          }
          public int getAtomicValue(){
          return mAtomicValue.get();
          }

          (2).代碼塊同步

          代碼塊同步分樂觀鎖和悲觀鎖來講解

          使用悲觀鎖時(shí),其他線程等待,進(jìn)入睡眠,頻繁切換任務(wù),消耗cpu資源

                  synchronized (this) {
          .....
          }

          使用樂觀鎖時(shí),失敗重試,避免任務(wù)重復(fù)切換,減少cpu消耗

                  ReentrantLock lock = new  ReentrantLock();
          lock.lock();
          ......
          lock.unlock();

          Thread注意點(diǎn)就講到這里,下面讓我們進(jìn)入今天的主題,多線程并發(fā)優(yōu)化。

          二、Android Thread

          android中很多操作需要在主線程中執(zhí)行,比如UI的操作,點(diǎn)擊事件等等,但是如果主線程操作太多,占有的執(zhí)行時(shí)間過長(zhǎng)就會(huì)出現(xiàn)前面我們說的卡頓現(xiàn)象:

          8f57e5381dbbdfd044a7e9bfa7404334.webp

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          為了減輕主線程操作過多,避免出現(xiàn)卡頓的現(xiàn)象,我們把一些操作復(fù)雜的消耗時(shí)間長(zhǎng)的任務(wù)放到線程池中去執(zhí)行。下面我們就來介紹android中幾種線程的類。

          1.AsyncTask

          為UI線程與工作線程之間進(jìn)行快速的切換提供一種簡(jiǎn)單便捷的機(jī)制。適用于當(dāng)下立即需要啟動(dòng),但是異步執(zhí)行的生命周期短暫的使用場(chǎng)景。
          它提供了一種簡(jiǎn)便的異步處理機(jī)制,但是它又同時(shí)引入了一些令人厭惡的麻煩。一旦對(duì)AsyncTask使用不當(dāng),很可能對(duì)程序的性能帶來負(fù)面影響,同時(shí)還可能導(dǎo)致內(nèi)存泄露。(關(guān)于內(nèi)存泄漏在上面已經(jīng)講過)

          • 使用AsyncTask需要注意的問題?

          (1).在AsyncTask中所有的任務(wù)都是被線性調(diào)度執(zhí)行的,他們處在同一個(gè)任務(wù)隊(duì)列當(dāng)中,按順序逐個(gè)執(zhí)行。一旦有任務(wù)執(zhí)行時(shí)間過長(zhǎng),隊(duì)列中其他任務(wù)就會(huì)阻塞。

          cbb79df9849db0ac2d8a8948c18857fc.webp

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          對(duì)于上面的問題,我們可以使用AsyncTask.executeOnExecutor()讓AsyncTask變成并發(fā)調(diào)度。

          (2).AsyncTask對(duì)正在執(zhí)行的任務(wù)不具備取消的功能,所以我們要在任務(wù)代碼中添加取消的邏輯(和上面Thread類似)

          (3).AsyncTask使用不當(dāng)會(huì)導(dǎo)致內(nèi)存泄漏(可以參考內(nèi)存泄漏一章)

          2.HandlerThread

          為某些回調(diào)方法或者等待某些任務(wù)的執(zhí)行設(shè)置一個(gè)專屬的線程,并提供線程任務(wù)的調(diào)度機(jī)制。
          先來了解下Looper,Handler,MessageQueue
          Looper: 能夠確保線程持續(xù)存活并且可以不斷的從任務(wù)隊(duì)列中獲取任務(wù)并進(jìn)行執(zhí)行。
          Handler: 能夠幫助實(shí)現(xiàn)隊(duì)列任務(wù)的管理,不僅僅能夠把任務(wù)插入到隊(duì)列的頭部,尾部,還可以按照一定的時(shí)間延遲來確保任務(wù)從隊(duì)列中能夠來得及被取消掉。
          MessageQueue: 使用Intent,Message,Runnable作為任務(wù)的載體在不同的線程之間進(jìn)行傳遞。
          把上面三個(gè)組件打包到一起進(jìn)行協(xié)作,這就是HandlerThread

          7958f29148330bfee38899cb3c6c0cf2.webp

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          我們先來看下源碼:

                  public class HandlerThread extends Thread {
          public HandlerThread(String name, int priority) {
          super(name);
          mPriority = priority;
          }

          @Override
          public void run() {
          mTid = Process.myTid();
          Looper.prepare();
          synchronized (this) {
          mLooper = Looper.myLooper();
          notifyAll();
          }
          Process.setThreadPriority(mPriority);
          onLooperPrepared();
          Looper.loop();
          mTid = -1;
          }

          public Looper getLooper() {
          if (!isAlive()) {
          return null;
          }
          // If the thread has been started, wait until the looper has been created.
          synchronized (this) {
          while (isAlive() && mLooper == null) {
          try {
          wait();
          } catch (InterruptedException e) {
          }
          }
          }
          return mLooper;
          }
          }

          從上面的源碼發(fā)現(xiàn),HandlerThread其實(shí)就是在線程中維持一個(gè)消息循環(huán)隊(duì)列。下面我們看下使用:

                  HandlerThread mHanderThread = new HandlerThread("hanlderThreadTest", Process.THREAD_PRIORITY_BACKGROUND);
          mHanderThread.run();
          Looper mHanderThreadLooper = mHanderThread.getLooper();

          Handler mHandler = new Handler(mHanderThreadLooper){
          @Override
          public void handleMessage(Message msg) {
          super.handleMessage(msg);
          //子線程中執(zhí)行
          ...
          }
          };
          //發(fā)送消息
          mHandler.post(new Runnable() {
          @Override
          public void run() {
          ...
          }
          });

          3.IntentService

          適合于執(zhí)行由UI觸發(fā)的后臺(tái)Service任務(wù),并可以把后臺(tái)任務(wù)執(zhí)行的情況通過一定的機(jī)制反饋給UI。
          默認(rèn)的Service是執(zhí)行在主線程的,可是通常情況下,這很容易影響到程序的繪制性能(搶占了主線程的資源)。除了前面介紹過的AsyncTask與HandlerThread,我們還可以選擇使用IntentService來實(shí)現(xiàn)異步操作。IntentService繼承自普通Service同時(shí)又在內(nèi)部創(chuàng)建了一個(gè)HandlerThread,在onHandlerIntent()的回調(diào)里面處理扔到IntentService的任務(wù)。所以IntentService就不僅僅具備了異步線程的特性,還同時(shí)保留了Service不受主頁面生命周期影響的特點(diǎn)。

          1acf55d7ad497ae33e9aa7908567ed9b.webp

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          使用IntentService需要特別注意的點(diǎn):

          (1).因?yàn)镮ntentService內(nèi)置的是HandlerThread作為異步線程,所以每一個(gè)交給IntentService的任務(wù)都將以隊(duì)列的方式逐個(gè)被執(zhí)行到,一旦隊(duì)列中有某個(gè)任務(wù)執(zhí)行時(shí)間過長(zhǎng),那么就會(huì)導(dǎo)致后續(xù)的任務(wù)都會(huì)被延遲處理。

          (2).通常使用到IntentService的時(shí)候,我們會(huì)結(jié)合使用BroadcastReceiver把工作線程的任務(wù)執(zhí)行結(jié)果返回給主UI線程。使用廣播容易引起性能問題,我們可以使用LocalBroadcastManager來發(fā)送只在程序內(nèi)部傳遞的廣播,從而提升廣播的性能。我們也可以使用runOnUiThread()快速回調(diào)到主UI線程。

          (3).包含正在運(yùn)行的IntentService的程序相比起純粹的后臺(tái)程序更不容易被系統(tǒng)殺死,該程序的優(yōu)先級(jí)是介于前臺(tái)程序與純后臺(tái)程序之間的。

          4.Loader

          對(duì)于3.0后ContentProvider中的耗時(shí)操作,推薦使用Loader異步加載數(shù)據(jù)機(jī)制。相對(duì)其他加載機(jī)制,Loader有那些優(yōu)點(diǎn)呢?

          • 提供異步加載數(shù)據(jù)機(jī)制

          • 對(duì)數(shù)據(jù)源變化進(jìn)行監(jiān)聽,實(shí)時(shí)更新數(shù)據(jù)

          • 在Activity配置發(fā)生變化(如橫豎屏切換)時(shí)不用重復(fù)加載數(shù)據(jù)

          • 適用于任何Activity和Fragment

          下面我們來看下Loader的具體使用:
          我們以獲得手機(jī)中所有的圖片為例:

                  getLoaderManager().initLoader(LOADER_TYPE, null, mLoaderCallback);
          LoaderManager.LoaderCallbacks<Cursor mLoaderCallback = new LoaderManager.LoaderCallbacks<Cursor() {
          private final String[] IMAGE_COLUMNS={
          MediaStore.Images.Media.DATA,//圖片路徑
          MediaStore.Images.Media.DISPLAY_NAME,//顯示的名字
          MediaStore.Images.Media.DATE_ADDED,//添加時(shí)間
          MediaStore.Images.Media.MIME_TYPE,//圖片擴(kuò)展類型
          MediaStore.Images.Media.SIZE,//圖片大小
          MediaStore.Images.Media._ID,//圖片id
          };

          @Override
          public Loader<Cursor onCreateLoader(int id, Bundle args) {
          toggleShowLoading(true,getString(R.string.common_loading));

          CursorLoader cursorLoader = new CursorLoader(ImageSelectActivity.this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,IMAGE_COLUMNS,
          IMAGE_COLUMNS[4] + " 0 AND "+IMAGE_COLUMNS[3] + " =? OR " +IMAGE_COLUMNS[3] + " =? ",
          new String[]{"image/jpeg","image/png"},IMAGE_COLUMNS[2] + " DESC");
          return cursorLoader;
          }

          @Override
          public void onLoadFinished(Loader<Cursor loader, Cursor data) {
          if(data != null && data.getCount() 0){
          ArrayList<String imageList = new ArrayList<();

          if(mShowCamera){
          imageList.add("");
          }
          while (data.moveToNext()){
          String path = data.getString(data.getColumnIndexOrThrow(IMAGE_COLUMNS[0]));
          imageList.add(path);
          Log.e("ImageSelect", "IIIIIIIIIIIIIIIIIIII====="+path);
          }
          //顯示數(shù)據(jù)
          showListData(imageList);
          toggleShowLoading(false,getString(R.string.common_loading));
          }
          }

          @Override
          public void onLoaderReset(Loader<Cursor loader) {
          }

          onCreateLoader() 實(shí)例化并返回一個(gè)新創(chuàng)建給定ID的Loader對(duì)象
          onLoadFinished() 當(dāng)創(chuàng)建好的Loader完成了數(shù)據(jù)的load之后回調(diào)此方法
          onLoaderReset() 當(dāng)創(chuàng)建好的Loader被reset時(shí)調(diào)用此方法,這樣保證它的數(shù)據(jù)無效
          LoaderManager會(huì)對(duì)查詢的操作進(jìn)行緩存,只要對(duì)應(yīng)Cursor上的數(shù)據(jù)源沒有發(fā)生變化,在配置信息發(fā)生改變的時(shí)候(例如屏幕的旋轉(zhuǎn)),Loader可以直接把緩存的數(shù)據(jù)回調(diào)到onLoadFinished(),從而避免重新查詢數(shù)據(jù)。另外系統(tǒng)會(huì)在Loader不再需要使用到的時(shí)候(例如使用Back按鈕退出當(dāng)前頁面)回調(diào)onLoaderReset()方法,我們可以在這里做數(shù)據(jù)的清除等等操作。

          5.ThreadPool

          把任務(wù)分解成不同的單元,分發(fā)到各個(gè)不同的線程上,進(jìn)行同時(shí)并發(fā)處理。
          線程池適合用在把任務(wù)進(jìn)行分解,并發(fā)進(jìn)行執(zhí)行的場(chǎng)景。
          系統(tǒng)提供ThreadPoolExecutor幫助類來幫助我們簡(jiǎn)化實(shí)現(xiàn)線程池。

          e674b1f97f2dbe37289877cf7d3df330.webp

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          使用線程池需要特別注意同時(shí)并發(fā)線程數(shù)量的控制,理論上來說,我們可以設(shè)置任意你想要的并發(fā)數(shù)量,但是這樣做非常的不好。因?yàn)镃PU只能同時(shí)執(zhí)行固定數(shù)量的線程數(shù),一旦同時(shí)并發(fā)的線程數(shù)量超過CPU能夠同時(shí)執(zhí)行的閾值,CPU就需要花費(fèi)精力來判斷到底哪些線程的優(yōu)先級(jí)比較高,需要在不同的線程之間進(jìn)行調(diào)度切換。
          一旦同時(shí)并發(fā)的線程數(shù)量達(dá)到一定的量級(jí),這個(gè)時(shí)候CPU在不同線程之間進(jìn)行調(diào)度的時(shí)間就可能過長(zhǎng),反而導(dǎo)致性能嚴(yán)重下降。另外需要關(guān)注的一點(diǎn)是,每開一個(gè)新的線程,都會(huì)耗費(fèi)至少64K+的內(nèi)存。為了能夠方便的對(duì)線程數(shù)量進(jìn)行控制,ThreadPoolExecutor為我們提供了初始化的并發(fā)線程數(shù)量,以及最大的并發(fā)數(shù)量進(jìn)行設(shè)置。

                  /**
          * 核心線程數(shù)
          * 最大線程數(shù)
          * 保活時(shí)間
          * 時(shí)間單位
          * 任務(wù)隊(duì)列
          * 線程工廠
          */

          threadPoolExecutor = new ThreadPoolExecutor(
          CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
          linkedBlockingQueue, sThreadFactory);
          threadPoolExecutor.execute(runnable);

          • 我們知道系統(tǒng)還提供了Executors類中幾種線程池,下面我們來看下這些線程池的缺點(diǎn):

          newFixedThreadPool 和 newSingleThreadExecutor:主要問題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至 OOM。
          newCachedThreadPool 和 newScheduledThreadPool:主要問題是線程數(shù)最大數(shù)是 Integer.MAX_VALUE,可能會(huì)創(chuàng)建數(shù)量非常多的線程,甚至 OOM

          • 我們看到這些線程池但是有缺點(diǎn)的,所以具體使用那種方式實(shí)現(xiàn)要根據(jù)我們的需求來選擇。

          如果想要避開上面的問題,可以參考OKHttp中線程池的實(shí)現(xiàn),OKHttp中隊(duì)線程調(diào)度又封裝了一層,使用安全且方便,有興趣的可以去看看源碼。

          三.線程優(yōu)先級(jí)

          Android系統(tǒng)會(huì)根據(jù)當(dāng)前運(yùn)行的可見的程序和不可見的后臺(tái)程序?qū)€程進(jìn)行歸類,劃分為forground的那部分線程會(huì)大致占用掉CPU的90%左右的時(shí)間片,background的那部分線程就總共只能分享到5%-10%左右的時(shí)間片。之所以設(shè)計(jì)成這樣是因?yàn)閒orground的程序本身的優(yōu)先級(jí)就更高,理應(yīng)得到更多的執(zhí)行時(shí)間。

          a1f739135ed165a7017725265b6d6d46.webp

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          默認(rèn)情況下,新創(chuàng)建的線程的優(yōu)先級(jí)默認(rèn)和創(chuàng)建它的母線程保持一致。如果主UI線程創(chuàng)建出了幾十個(gè)工作線程,這些工作線程的優(yōu)先級(jí)就默認(rèn)和主線程保持一致了,為了不讓新創(chuàng)建的工作線程和主線程搶占CPU資源,需要把這些線程的優(yōu)先級(jí)進(jìn)行降低處理,這樣才能給幫組CPU識(shí)別主次,提高主線程所能得到的系統(tǒng)資源。

          在Android系統(tǒng)里面,我們可以通過android.os.Process.setThreadPriority(int)設(shè)置線程的優(yōu)先級(jí),參數(shù)范圍從-20到24,數(shù)值越小優(yōu)先級(jí)越高。Android系統(tǒng)還為我們提供了以下的一些預(yù)設(shè)值,我們可以通過給不同的工作線程設(shè)置不同數(shù)值的優(yōu)先級(jí)來達(dá)到更細(xì)粒度的控制。

          a637e27c17bd639f2daac75f3f777126.webp

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          大多數(shù)情況下,新創(chuàng)建的線程優(yōu)先級(jí)會(huì)被設(shè)置為默認(rèn)的0,主線程設(shè)置為0的時(shí)候,新創(chuàng)建的線程還可以利用THREAD_PRIORITY_LESS_FAVORABLE或者THREAD_PRIORITY_MORE_FAVORABLE來控制線程的優(yōu)先級(jí)。

          參考鏈接:https://www.jianshu.com/p/2795aef8980d

          至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺得很優(yōu)秀,歡迎點(diǎn)擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請(qǐng)聯(lián)系小編刪除,歡迎您的建議與指正。同時(shí)期待您的關(guān)注,感謝您的閱讀,謝謝!

          瀏覽 56
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级A毛片| 国产精品人妻熟女毛片av | 国产成人精品 视频 | 免费国产日本 | 大香蕉中文青草 |