Java實(shí)戰(zhàn)開(kāi)發(fā)篇-11 多線程

多線程

一、多線程中的幾個(gè)概念

1.程序:靜態(tài)的代碼
2.進(jìn)程:正在運(yùn)行的一個(gè)程序 正在使用的QQ,Android Studio。進(jìn)程用于管理所有的資源,不進(jìn)行實(shí)際的任務(wù)
3.線程:完成具體任務(wù),QQ運(yùn)行起來(lái)就是線程(一個(gè)進(jìn)程里面可以有多個(gè)線程)。運(yùn)行QQ,聊天、視頻、QQ游戲同時(shí)運(yùn)行,這就是一個(gè)個(gè)線程
4.主線程:Java里面,main方法里面的代碼就在主線程中運(yùn)行。在手機(jī)里面,我們看到的主界面,就是一個(gè)主線程
5.子線程:除了主線程之外的線程

二、為什么使用多線程

在主線程里面,任務(wù)的執(zhí)行是從上至下的,如果其中一個(gè)任務(wù)需要耗費(fèi)大量時(shí)間。那么這個(gè)任務(wù)后面的任務(wù)就必須等這個(gè)任務(wù)結(jié)束后才能被執(zhí)行,就會(huì)形成阻塞。這個(gè)時(shí)候就需要將這個(gè)任務(wù)放在另一個(gè)線程里面去執(zhí)行(子線程)
*注:不管是主線程還是子線程都有自己獨(dú)立的執(zhí)行路徑

三、如何開(kāi)啟一個(gè)線程

1.寫一個(gè)類繼承于Thread

步驟:
(1)創(chuàng)建類繼承于Thread,具體執(zhí)行的任務(wù)放在run()里面
(2)創(chuàng)建類的對(duì)象
(3)調(diào)用start()方法執(zhí)行
注*線程的執(zhí)行是通過(guò)搶占時(shí)間片來(lái)獲取執(zhí)行機(jī)會(huì)的,時(shí)間片是由操作系統(tǒng)來(lái)分配的
所以有多個(gè)線程的時(shí)候,每次執(zhí)行的結(jié)果可能不一樣

class TestThread extends Thread{
    //1.創(chuàng)建一個(gè)類繼承于Thread
    //可以通過(guò)重寫構(gòu)造方法給子線程命名
    public TestThread(@NonNull String name) {
        super(name);
    }
    @Override
    //子類必須實(shí)現(xiàn)父類的run方法,這個(gè)線程執(zhí)行的任務(wù)在run方法里面
    public void run() {
        System.out.println(getName());
        //也可以用Thread.currentThread(),獲取當(dāng)前線程的名字
        System.out.println("Hello World");
    }
}
public class MyClass {
    public static void main(String[] args){
         //2.創(chuàng)建具體的對(duì)象
        TestThread testThread = new TestThread("子線程");//給子線程命名
        //3.啟動(dòng)線程,不調(diào)用start無(wú)法啟動(dòng)線程
        testThread.start();
}
}
    public static void testRunnable(){
        //2.創(chuàng)建具體對(duì)象
        TestRunnable testRunnable = new TestRunnable();
        //3.創(chuàng)建一個(gè)Thread對(duì)象 讓這個(gè)線程去執(zhí)行testRunnable的任務(wù)
        Thread thread = new Thread(testRunnable);
        thread.start();
    }
}

2.寫一個(gè)類實(shí)現(xiàn)Runnable接口

步驟:
(1)創(chuàng)建一個(gè)類實(shí)現(xiàn)Runnable接口,但它并不能分配線程
(2)創(chuàng)建一個(gè)該類的對(duì)象
(3)創(chuàng)建Thread類的對(duì)象來(lái)創(chuàng)建線程
(4)調(diào)用start方法開(kāi)啟線程

class TestRunnable implements Runnable{
    //1.創(chuàng)建一個(gè)類實(shí)現(xiàn)Runnable方法
    //這個(gè)類不能開(kāi)啟線程,也需要通過(guò)Thread來(lái)開(kāi)啟
    @Override
    public void run() {
        System.out.println("Hello World");
    }
}
public class MyClass {
    public static void main(String[] args){
        //2.創(chuàng)建具體對(duì)象
        TestRunnable testRunnable = new TestRunnable();
        //3.創(chuàng)建一個(gè)Thread對(duì)象 讓這個(gè)線程去執(zhí)行testRunnable的任務(wù)
        Thread thread = new Thread(testRunnable);
        thread.start();
}
}

3.兩種啟動(dòng)方式的對(duì)比

第一種創(chuàng)建方法簡(jiǎn)單一些,但是無(wú)法實(shí)現(xiàn)多繼承
第二種靈活性更強(qiáng),因?yàn)榻涌诳梢詫?shí)現(xiàn)多繼承

四、線程的生命周期

線程的5種形態(tài)

new創(chuàng)建狀態(tài)->start就緒狀態(tài)-><-搶到時(shí)間片,運(yùn)行狀態(tài)(失去時(shí)間片進(jìn)入就緒狀態(tài))->run死亡狀態(tài)
運(yùn)行狀態(tài)中,可能遇到阻塞狀態(tài)

1.創(chuàng)建狀態(tài)

new Thread()

2.就緒狀態(tài)

(1)調(diào)用start()
(2)阻塞條件結(jié)束
(3)正在運(yùn)行的線程時(shí)間片被其他線程所搶奪

3.運(yùn)行狀態(tài)

從就緒狀態(tài)到運(yùn)行狀態(tài)是由操作系統(tǒng)進(jìn)行,外部無(wú)法干預(yù)

4.死亡狀態(tài)

(1)run方法結(jié)束
(2)手動(dòng)讓線程暫停 stop(不建議使用,通過(guò)其他方式暫停)

5.阻塞狀態(tài)

阻塞狀態(tài)分為3種,同步阻塞synchronized,等待阻塞wait,其他阻塞sleep,join

五、如何讓一個(gè)線程結(jié)束

(1)使用stop()方法;(此方法不建議使用)
(2)寫一個(gè)變量來(lái)標(biāo)識(shí)線程結(jié)束

class TestThread extends Thread{
    boolean stop = true;
    @Override
   public void run() {
        while (stop) {
   System.out.println("子線程");
    }
    }
   public void terminated(){
        stop = false;
        }//寫一個(gè)終止方法
}
public class MyClass {
    public static void main(String[] args){
    TestThread t = new TestThread();
    t.start();
    for(int i = 0;i<20;i++){
        if(i==10){
            t.terminated();
    }
    }
}
}

六、線程禮讓和線程插隊(duì)

線程禮讓:yield()
線程插隊(duì):join()
禮讓的線程會(huì)直接進(jìn)入就緒狀態(tài),如果這個(gè)線程再次獲得時(shí)間片,它還會(huì)執(zhí)行,所以可能禮讓失敗

TestRunnable testRunnable = new TestRunnable();
    Thread t1 = new Thread(testRunnable,"子線程1");
     t1.start();
     for(int i = 0;i<100;i++){
     System.out.println("主線程");
     if(i == 20){
     Thread.yield();
  }
  }
public class MyClass {
    public static void main(String[] args){
    TestThread t = new TestThread();
    t.start();
}
}

插隊(duì)同理,插隊(duì)后的線程執(zhí)行完后再執(zhí)行原線程

七、多線程的利弊

1.優(yōu)點(diǎn)

(1)提高執(zhí)行的效率
(2)不會(huì)阻塞主線程

2.缺點(diǎn)

(1)多個(gè)線程操作同一個(gè)資源時(shí),有可能會(huì)出錯(cuò)

class BuyTickets extends Thread{
  static int total = 10;
    @Override
    public void run() {
       for(int i = 1;i<11;i++){
       if (total == 0) {
       stop();
    }
    }
       total--;
       System.out.println("第"+(10-total)+"張票購(gòu)成功");
    }
    }
}
public class MyClass {
    public static void main(String[] args){
       BuyTickets Passenger1 = new BuyTickets();
       BuyTickets Passenger2 = new BuyTickets();
       BuyTickets Passenger3 = new BuyTickets();
       Passenger1.start();
       Passenger2.start();
       Passenger3.start();
    }
}

3.克服缺點(diǎn)的辦法

(1)Lock鎖,代碼塊必須使用同一把鎖
(2)2.synchronized鎖

class BuyTickets extends Thread{
    Object object = new Object();//創(chuàng)建一個(gè)臨時(shí)對(duì)象
  static int total = 10;
    @Override
    public void run() {
      for(int i = 1;i<11;i++){
      synchronized (object) {
      if (total == 0) {
      stop();
    }
    }
      total--;
      System.out.println("第"+(10-total)+"張票購(gòu)成功");
    }
    }
}
public class MyClass {
    public static void main(String[] args){
       BuyTickets Passenger1 = new BuyTickets();
       BuyTickets Passenger2 = new BuyTickets();
       BuyTickets Passenger3 = new BuyTickets();
       Passenger1.start();
       Passenger2.start();
       Passenger3.start();
    }
}

*注:鎖的對(duì)象必須相同,每一個(gè)對(duì)象都維護(hù)一把鎖
不管是鎖代碼塊還是鎖方法,盡量讓鎖的范圍變小

八、線程間的通信

1.實(shí)現(xiàn)線程間通信的三個(gè)方法

(1)wait()讓某個(gè)線程等待
(2)notify()喚醒某個(gè)線程
(3)notifyAll()喚醒所有線程
*注:這三個(gè)方法必須由同步監(jiān)視器(必須被Lock或synchronized包裝的代碼塊)來(lái)調(diào)用

public class MyClass {
    static intersection section = new intersection();
    public static void main(String[] args){
        new Thread(new Runnable() {
            @Override
            public void run() {
                section.printnum();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                section.printlet();
            }
        }).start();
    }
}//匿名對(duì)象和匿名內(nèi)部類快速創(chuàng)建對(duì)象和調(diào)用方法
class intersection{
    int number = 1;
    char letter = 'a';
    int state = 1;//通過(guò)變化state的值來(lái)改變線程
    public synchronized void printnum(){
    while(true){
       if(state != 1){
       try {
       this.wait();
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
  }
    System.out.println(number);
    number++;
      if(number == 27){
      break;
  }
      state = 2;
      this.notify();
  }
  }
  public synchronized void printlet(){
        while(true){
        if(state != 2){
        try {
        this.wait();
   } catch (InterruptedException e) {
     e.printStackTrace();
   }
   }
    System.out.println(letter);
    letter++;
     if(letter == ('z'+1)){
     break;
    }
     state = 1;
     this.notify();
    }
    }
}
最后編輯于
?著作權(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)容