多線程

1 繼承Thread類

image.png

2 實(shí)現(xiàn)Runnable接口

image.png

a.常規(guī)使用

// 步驟1:創(chuàng)建線程輔助類,實(shí)現(xiàn)Runnable接口
 class MyThread implements Runnable{
    ....
    @Override
// 步驟2:復(fù)寫run(),定義線程行為
    public void run(){

    }
}

// 步驟3:創(chuàng)建線程輔助對象,即 實(shí)例化 線程輔助類
  MyThread mt=new MyThread();

// 步驟4:創(chuàng)建線程對象,即 實(shí)例化線程類;線程類 = Thread類;
// 創(chuàng)建時通過Thread類的構(gòu)造函數(shù)傳入線程輔助類對象
// 原因:Runnable接口并沒有任何對線程的支持,我們必須創(chuàng)建線程類(Thread類)的實(shí)例,從Thread類的一個實(shí)例內(nèi)部運(yùn)行
  Thread td=new Thread(mt);

// 步驟5:通過 線程對象 控制線程的狀態(tài),如 運(yùn)行、睡眠、掛起  / 停止
// 當(dāng)調(diào)用start()方法時,線程對象會自動回調(diào)線程輔助類對象的run(),從而實(shí)現(xiàn)線程操作
  td.start();

b.匿名類

    // 步驟1:通過匿名類 直接 創(chuàng)建線程輔助對象,即 實(shí)例化 線程輔助類
    Runnable mt = new Runnable() {
                    // 步驟2:復(fù)寫run(),定義線程行為
                    @Override
                    public void run() {
                    }
                };

    // 步驟3:創(chuàng)建線程對象,即 實(shí)例化線程類;線程類 = Thread類;
    Thread mt1 = new Thread(mt, "窗口1");
    // 步驟4:通過 線程對象 控制線程的狀態(tài),如 運(yùn)行、睡眠、掛起  / 停止
    mt1.start();

2.1 Thread和Runnable的區(qū)別

image.png

3 Handler

image.png

image.png

3.1 Handler工作原理

工作流程

1.異步通信準(zhǔn)備
2.消息發(fā)送
3.消息循環(huán)
4.消息處理

image.png

3.2 工作流程

image.png

image.png

3.3 使用Handler.sendMessage()

class mHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    }

    void useHandler() {
        Handler mHandler = new mHandler();
        
        Message message = Message.obtain();
        message.what = 1;
        message.obj = "AA";
        
        mHandler.sendMessage(message);
   }

3.4 使用Handler.post

void useHandler() {
        Handler mHandler = new mHandler();
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                
            }
        });
    }

3.5 具體類

image.png

4 AsyncTask

屬于抽象類 使用時要實(shí)現(xiàn)子類

4.1 作用

1 實(shí)現(xiàn)多線程
在工作線程中執(zhí)行任務(wù)
2 異步通信 消息傳遞
實(shí)現(xiàn)工作線程和主線程直接的通信

4.2 優(yōu)點(diǎn)

  • 方便實(shí)現(xiàn)異步通信
    不需要使用 "任務(wù)線程 + Handler" 的復(fù)雜組合
  • 節(jié)省資源
    采用線程池的緩存線程 + 復(fù)用線程 避免了頻繁創(chuàng)建和銷毀線程所帶來的系統(tǒng)資源開銷

4.3 類和方法

4.3.1 類定義
public abstract class AsyncTask<Params, Progress, Result> { 
 ... 
}

// 類中參數(shù)為3種泛型類型
// 整體作用:控制AsyncTask子類執(zhí)行線程任務(wù)時各個階段的返回類型
// 具體說明:
    // a. Params:開始異步任務(wù)執(zhí)行時傳入的參數(shù)類型,對應(yīng)excute()中傳遞的參數(shù)
    // b. Progress:異步任務(wù)執(zhí)行過程中,返回下載進(jìn)度值的類型
    // c. Result:異步任務(wù)執(zhí)行完成后,返回的結(jié)果類型,與doInBackground()的返回值類型保持一致
// 注:
    // a. 使用時并不是所有類型都被使用
    // b. 若無被使用,可用java.lang.Void類型代替
    // c. 若有不同業(yè)務(wù),需額外再寫1個AsyncTask的子類
}
4.3.2 核心方法
image.png
  • 方法執(zhí)行順序


    image.png

4.4 使用步驟

  1. 創(chuàng)建AsyncTask 子類 和 根據(jù)需求實(shí)現(xiàn)核心方法
    2 創(chuàng)建AsyncTask 子類的實(shí)例對象
    3 手動調(diào)用execute() 從而執(zhí)行異步線程任務(wù)
public class MyTask extends AsyncTask<String, Integer, String> {

    //執(zhí)行線程前的操作
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    //接受輸入?yún)?shù)  執(zhí)行任務(wù)中的耗時操作 返回線程任務(wù)執(zhí)行的結(jié)果
    @Override
    protected String doInBackground(String... strings) {
        return null;
    }

    //在主線程 顯示線程任務(wù)執(zhí)行的進(jìn)度
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    //接受線程任務(wù)執(zhí)行結(jié)果 將執(zhí)行結(jié)果顯示到UI組件
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
    }

    //將異步任務(wù)設(shè)置為取消狀態(tài)
    @Override
    protected void onCancelled() {
        super.onCancelled();
    }
}

4.5 生命周期

AsyncTask 不與任何組件綁定聲明周期
在Activity 或 Fragment中使用 AsyncTask時,最好在Activity 或 Fragment的onDestory()調(diào)用 cancel(boolean)

4.6 內(nèi)存泄露

若AsyncTask被聲明為Activity的非靜態(tài)內(nèi)部類,當(dāng)Activity需銷毀時,會因AsyncTask保留對Activity的引用 而導(dǎo)致Activity無法被回收,最終引起內(nèi)存泄露

把AsyncTask聲明為Activity的靜態(tài)內(nèi)部類

4.7 線程任務(wù)執(zhí)行結(jié)果 丟失

當(dāng)Activity重新創(chuàng)建時(屏幕旋轉(zhuǎn) / Activity被意外銷毀時后恢復(fù)),之前運(yùn)行的AsyncTask(非靜態(tài)的內(nèi)部類)持有的之前Activity引用已無效,故復(fù)寫的onPostExecute()將不生效,即無法更新UI操作

在Activity恢復(fù)時的對應(yīng)方法 重啟 任務(wù)線程

4.8 工作原理

AsyncTask的實(shí)現(xiàn)原理 = 線程池 + Handler


image.png

5. ThreadPool

image.png

image.png

5.1 內(nèi)部原理邏輯

image.png

5.2 常見的4類功能線程池

  • 定長線程池(FixedThreadPool)
  • 定時線程池(ScheduledThreadPool )
  • 可緩存線程池(CachedThreadPool)
  • 單線程化線程池(SingleThreadExecutor)
5.2.1 定長線程池

只有核心線程 不會被回收 線程數(shù)量固定 任務(wù)隊列無大小限制(超出的線程任務(wù)會在隊列中等待)
應(yīng)用場景:控制線程最大并發(fā)數(shù)

       //創(chuàng)建定長線程池對象 設(shè)置線程池數(shù)量固定為3
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        //創(chuàng)建好Runnable
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("執(zhí)行任務(wù)");
            }
        };
        // 向線程池提交任務(wù)
        fixedThreadPool.execute(task);
        // 關(guān)閉線程池
        fixedThreadPool.shutdown();
5.2.2 定時線程池

核心線程數(shù)量固定、非核心線程數(shù)量無限制(閑置時馬上回收)
執(zhí)行定時 / 周期性 任務(wù)

      // 1. 創(chuàng)建 定時線程池對象 & 設(shè)置線程池線程數(shù)量固定為5
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        //創(chuàng)建好Runnable
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("執(zhí)行任務(wù)");
            }
        };
        // 向線程池提交任務(wù)
        scheduledExecutorService.schedule(task,1, TimeUnit.SECONDS);// 延遲1s后執(zhí)行任務(wù)
        //延遲10毫秒 每隔1000毫秒執(zhí)行任務(wù)
        scheduledExecutorService.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);
        //關(guān)閉線程
        scheduledExecutorService.shutdown();
5.2.3 可緩存線程池

只有非核心線程,線程數(shù)量不固定(可無限大),靈活回收空閑線程(具備超時機(jī)制,全部回收時幾乎不占系統(tǒng)資源),新建線程(無限城可用時)
任何線程任務(wù)到來都會立刻執(zhí)行,不需要等待
執(zhí)行大量、耗時少的線程任務(wù)

// 1. 創(chuàng)建可緩存線程池對象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

// 2. 創(chuàng)建好Runnable類線程對象 & 需執(zhí)行的任務(wù)
Runnable task =new Runnable(){
  public void run(){
        System.out.println("執(zhí)行任務(wù)啦");
            }
    };

// 3. 向線程池提交任務(wù):execute()
cachedThreadPool.execute(task);

// 4. 關(guān)閉線程池
cachedThreadPool.shutdown();

//當(dāng)執(zhí)行第二個任務(wù)時第一個任務(wù)已經(jīng)完成
//那么會復(fù)用執(zhí)行第一個任務(wù)的線程,而不用每次新建線程。
5.2.4 單線程化線程池

只有一個核心線程(保證所有任務(wù)按照指定順序在一個線程中執(zhí)行,不需要處理線程同步的問題)
不適合并發(fā)但可能引起IO阻塞及影響UI線程的操作

// 1. 創(chuàng)建單線程化線程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

// 2. 創(chuàng)建好Runnable類線程對象 & 需執(zhí)行的任務(wù)
Runnable task =new Runnable(){
  public void run(){
        System.out.println("執(zhí)行任務(wù)啦");
            }
    };

// 3. 向線程池提交任務(wù):execute()
singleThreadExecutor.execute(task);

// 4. 關(guān)閉線程池
singleThreadExecutor.shutdown();

5.3 線程池對比

image.png

5.4 多線程對比

image.png

6. Synchronized關(guān)鍵字

image.png

6.1 原理

  1. 依賴JVM實(shí)現(xiàn)同步
    2.底層通過一個監(jiān)視器對象(monitor)完成,wait() notify() 等方法也依賴于monitor對象

監(jiān)視器鎖(monitor)的本質(zhì) 依賴于 底層操作系統(tǒng)的互斥鎖(Mutex Lock)實(shí)現(xiàn)

6.2 鎖的類型和等級

image.png
6.2.1 區(qū)別
image.png

6.3 使用規(guī)則

image.png
//對象鎖
class Test {
    //方法鎖
    public synchronized void Method1() {
        System.out.println("我是對象鎖也是方法鎖");
        try {
            Thread.sleep(500);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //代碼塊形式
    public void Method2() {
        synchronized (this) {
            System.out.println("我是對象鎖");
            try {
                Thread.sleep(500);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
}
//方法鎖
    public synchronized void Method1() {
        System.out.println("我是對象鎖也是方法鎖");
        try {
            Thread.sleep(500);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
//類鎖
class Test {
    //鎖靜態(tài)方法
    public static synchronized void Method1() {
        System.out.println("我是對象鎖也是方法鎖");
        try {
            Thread.sleep(500);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //鎖靜態(tài)代碼塊
    public void Method2() {
        synchronized (Test.class){
            System.out.println("我是類鎖二號");
            try{
                Thread.sleep(500);
            } catch (InterruptedException e){
                e.printStackTrace();
            }

        }
    }

}

6.4 特別注意

若使用synchronized 關(guān)鍵字修飾線程類的run() 由于run()在線程的整個生命周期內(nèi)一直在運(yùn)行,因此將會導(dǎo)致它對本類恩和synchronized 方法的調(diào)用都永遠(yuǎn)不會成功

  • 解決方案
    使用 Synchronized關(guān)鍵字聲明代碼塊
 synchronized(syncObject) { 
    // 訪問或修改被鎖保護(hù)的共享狀態(tài) 
    // 上述方法 必須 獲得對象 syncObject(類實(shí)例或類)的鎖
}

6.5 特點(diǎn)

image.png

7. ThreadLocal

image.png

7.1 使用流程

7.1.1 創(chuàng)建ThreadLocal變量
//1.直接創(chuàng)建對象
    private ThreadLocal myThreadLocal = new ThreadLocal();
    //2.創(chuàng)建泛型對象
    private ThreadLocal threadLocal = new ThreadLocal<String>();
    //3.創(chuàng)建泛型對象 & 初始化值
    //指定泛型的好處 不需要每次對使用get()方法返回的值作強(qiáng)制類型轉(zhuǎn)換
    private ThreadLocal local = new ThreadLocal<String>() {
        @Nullable
        @Override
        protected String initialValue() {
            return "This is the initial value";
        }
    };

1.ThreadLocal 實(shí)例 = 類中的private static字段
2.只需實(shí)例化對象一次 & 不需要知道它是被哪個線程實(shí)例化
3.每個線程都保持 對其他線程局部變量副本的隱式引用
4.線程消失后 其線程舉報變量實(shí)例的所有副本都會被垃圾回收(除非存在對這些副本的其他引用)
5.雖然所有的線程都能訪問到這個ThreadLocal實(shí)例 但是每個線程只能訪問到自己通過調(diào)研ThreadLocal的set()設(shè)置的值(哪怕2個不同的線程在同一個ThreadLocal對象上設(shè)置了不同的值,他們?nèi)匀粺o法訪問到對方的值)

7.1.2 訪問ThreadLocal變量
// 1. 設(shè)置值:set()
// 需要傳入一個Object類型的參數(shù)
myThreadLocal.set("初始值”);

// 2. 讀取ThreadLocal變量中的值:get()
// 返回一個Object對象
String threadLocalValue = (String) myThreadLocal.get();

7.2 具體使用

class ThreadLocalTest {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable,"線程1").start();
        new Thread(runnable,"線程2").start();
    }

    public static class MyRunnable implements Runnable {

        private ThreadLocal<String> threadLocal = new ThreadLocal<>() {
            @Nullable
            @Override
            protected Object initialValue() {
                return "初始化值";
            }
        };

        @Override
        public void run() {
            //運(yùn)行線程時 分別設(shè)置 & 獲取ThreadLocal的值
            String name = Thread.currentThread().getName();
            threadLocal.set(name + "的threadLocal");
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + ":" + threadLocal.get());
        }
    }
}

7.3 實(shí)現(xiàn)原理

ThreadLocal類中有1個Map(稱:ThreadLocalMap):用于存儲每個線程 & 該線程設(shè)置的存儲在ThreadLocal變量的值

1.ThreadLocalMap的鍵key = 當(dāng)前ThreadLocal實(shí)例、值value = 該線程設(shè)置的存儲在ThreadLocal變量的值
2.該key是 ThreadLocal對象的弱引用;當(dāng)要拋棄掉ThreadLocal對象時,垃圾收集器會忽略該 key的引用而清理掉ThreadLocal對象

7.4 與同步機(jī)制的區(qū)別

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第5章 多線程編程 5.1 線程基礎(chǔ) 5.1.1 如何創(chuàng)建線程 在java要創(chuàng)建線程,一般有==兩種方式==:1)...
    AndroidMaster閱讀 1,906評論 0 11
  • Android中實(shí)現(xiàn)多線程,常見的方法有: 繼承Thread類 實(shí)現(xiàn)Runnable接口 ThreadPoolEx...
    FelixLiuu閱讀 704評論 0 0
  • 擴(kuò)展文章非主線程中能不能直接new Handler()Android 異步消息處理機(jī)制 讓你深入理解 Looper...
    wayDevelop閱讀 975評論 0 0
  • Java 多線程 線程和進(jìn)程的區(qū)別 線程和進(jìn)程的本質(zhì):由CPU進(jìn)行調(diào)度的并發(fā)式執(zhí)行任務(wù),多個任務(wù)被快速輪換執(zhí)行,使...
    安安zoe閱讀 2,260評論 1 18
  • CPU時間片輪轉(zhuǎn)機(jī)制 CPU時間片輪轉(zhuǎn)機(jī)制(RR調(diào)度-Round-robin scheduling)原理解釋如下:...
    任振銘閱讀 1,075評論 0 10

友情鏈接更多精彩內(nèi)容