一、多線程基礎(chǔ)

多線程基礎(chǔ)

基本概念

同步、異步

  • 同步 線程B要等待線程A的執(zhí)行結(jié)果之后才能執(zhí)行。要順序執(zhí)行
image
  • 異步 相對(duì)同步來說,彼此獨(dú)立,在在等待某事件的過程中可以繼續(xù)做自己的事。
image

這個(gè)更直觀 小人相當(dāng)于一個(gè)線程

image

并行/并發(fā)

并發(fā)和并行是異步的兩種實(shí)現(xiàn)形式,并行是真正的異步

  • 并發(fā)

    • 單核cpu情況,同一時(shí)刻只能分配給一個(gè)線程使用。其他等待。是邏輯層面的同時(shí)工作
    image
  • 并行

    • 多核cpu情況下,每個(gè)cpu都可以分配一個(gè)線程使用,同一時(shí)刻執(zhí)行多個(gè)并發(fā)任務(wù)。叫并行


      image

線程狀態(tài)

image

線程狀態(tài)共5種:創(chuàng)建、就緒、運(yùn)行、==阻塞(阻塞、等待、鎖定)==、終止。

創(chuàng)建線程

  • 繼承Thread類

public class A extends Thread {
    //繼承Thread類 實(shí)現(xiàn)run接口
    @Override
    public void run() {
        System.out.println("123");
    }

    public static void main(String[] args) {
        A a = new A();//創(chuàng)建線程,線程進(jìn)入創(chuàng)建狀態(tài)
        a.start();//開啟線程,線程進(jìn)入runnable
    }
}

  • 實(shí)現(xiàn)Runable接口
public class A implements Runnable {
    //繼承Thread類 實(shí)現(xiàn)run接口
    @Override
    public void run() {
        System.out.println("123");
    }

    public static void main(String[] args) {
        //創(chuàng)建一個(gè)runable實(shí)現(xiàn)類的對(duì)象
        A a = new A();
        //不是啟動(dòng)線程,只是方法調(diào)用
       a.run();
    }
}
  • 實(shí)現(xiàn)Callable接口
public class A implements Callable<Integer> {
    //繼承Callable類 實(shí)現(xiàn)call接口 call具有返回值
    @Override
    public Integer call() throws Exception {
        return 123;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創(chuàng)建一個(gè)callable實(shí)現(xiàn)類的對(duì)象
        A a = new A();
        //用FutureTask封裝
        FutureTask<Integer> future = new FutureTask<Integer>(a);
        //創(chuàng)建一個(gè)線程
        Thread thread = new Thread(future);
        thread.start();
        //線程啟動(dòng)后可以獲取返回值
        Integer integer = future.get();
        System.out.println(integer);
    }
}

實(shí)現(xiàn)Runnable接口比繼承Thread類優(yōu)勢(shì):

  • 線程池只能放入實(shí)現(xiàn)Runable的

  • 避免單繼承的限制

  • 代碼被多個(gè)線程共享,代碼和數(shù)據(jù)獨(dú)立,例如

    • 繼承Thread
int ticket=10;

MyThread t1=new MyThread();
t1.start();
MyThread t2=new MyThread();
t2.start();
//上面的代碼  就會(huì)賣出20張票 沒有數(shù)據(jù)共享
    • 實(shí)現(xiàn)接口
int ticket=10;

MyThread t=new MyThread();
t.run();
t.run(); 
//這樣多個(gè)線程的時(shí)候,就能實(shí)現(xiàn)數(shù)據(jù)共享

線程其他狀態(tài)

  • Runnable(就緒)

    線程調(diào)用start()方法,狀態(tài)為可執(zhí)行,等待獲取cpu的使用權(quán)

  • Running(運(yùn)行狀態(tài))

    獲得cpu的使用權(quán),執(zhí)行代碼。

  • Blocked(阻塞)

    • 等待阻塞

      執(zhí)行wait()方法,jvm把該線程放入==等待池==

    • 同步阻塞

      獲取同步鎖時(shí),鎖被其他線程占用,jvm把該線程放入==鎖池==

    • 其他阻塞

      執(zhí)行sleep()或join()或發(fā)出i/o請(qǐng)求時(shí),jvm把線程設(shè)置為阻塞狀態(tài)

常用函數(shù)說明

  1. sleep()

    • 在指定的毫秒數(shù)內(nèi)讓線程休眠,進(jìn)入阻塞狀態(tài)。
  2. join()

    • 主線程需要等待調(diào)用join的子線程執(zhí)行完畢。
  3. yield() 退讓

    • 暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程,該線程進(jìn)入runnable狀態(tài)
    • 目的:是為了讓相同優(yōu)先級(jí)的線程之間輪轉(zhuǎn)執(zhí)行。
    • 實(shí)際情況下,并不一定有效果,因?yàn)樽尣街?,該線程還是有可能被再次選中
sleep yield
在指定時(shí)間內(nèi)肯定不會(huì)被執(zhí)行 隨時(shí)都可以獲取鎖
允許較低優(yōu)先級(jí)的線程獲得運(yùn)行機(jī)會(huì) 只能同級(jí)
  1. interrupt() 中斷
    • 并不能中斷線程!
    • 只是給線程發(fā)出一個(gè)中斷信號(hào)。
  2. wait()休眠
    • 線程在獲取對(duì)象鎖之后,釋放對(duì)象鎖,所以必須是再synchronized{}中
  3. notify() 喚醒
    • 線程對(duì)對(duì)象鎖的喚醒操作。也要和synchronized{}搭配使用
    • notify并不能馬上獲取對(duì)象鎖,而是再synchronized語句塊結(jié)束,自動(dòng)釋放鎖之后,jvm在所有的wait()的線程中隨機(jī)選取。
  • 有個(gè)經(jīng)典的題,就是三個(gè)線程。分別打印10次A、B、C,要求順序執(zhí)行

多種實(shí)現(xiàn)

思路如圖
image
public class MyThreadPrinter implements Runnable {
    private String name;
    private Object prev;
    private Object self;

    /**
     *
     * @param name   需要打印的東西
     * @param prev   上一個(gè)對(duì)象
     * @param self   自己
     */
    private MyThreadPrinter(String name, Object prev, Object self) {
        this.name = name;
        this.prev = prev;
        this.self = self;
    }

    @Override
    public void run() {
        int count = 10;
        while (count > 0) {
            //需要獲取上一個(gè)對(duì)象的鎖和本身的鎖
            synchronized (prev) {
                synchronized (self) {
//重點(diǎn)在這
                    //打印本身的信息
                    System.out.print(name);
                    count--;
                    // 打印完 name了 可以喚醒了  因?yàn)樯弦粋€(gè)線程把本身給wait了
                    self.notify();
                }
                try {
                   // 釋放鎖 等待notify才可以重新獲取鎖
                    //比如a執(zhí)行完 需要執(zhí)行b 需要 b a鎖 就把c給wait
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();

        MyThreadPrinter pa = new MyThreadPrinter("A", c, a);
        //pa執(zhí)行完 c就wait了 只有a b可以獲取鎖
        MyThreadPrinter pb = new MyThreadPrinter("B", a, b);
        MyThreadPrinter pc = new MyThreadPrinter("C", b, c);

        new Thread(pa).start();
        Thread.sleep(100);  //確保按順序A、B、C執(zhí)行
        new Thread(pb).start();
        Thread.sleep(100);
        new Thread(pc).start();
        Thread.sleep(100);
    }

}
  • wait / sleep
wait sleep
釋放鎖 沒有釋放鎖
只能在同步快sync中使用 任何地方
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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