線程基本運用

24.01_多線程(多線程的引入)(了解)

  • 1.什么是線程
    • 線程是程序執(zhí)行的一條路徑, 一個進(jìn)程中可以包含多條線程
    • 多線程并發(fā)執(zhí)行可以提高程序的效率, 可以同時完成多項工作
  • 2.多線程的應(yīng)用場景
    • 紅蜘蛛同時共享屏幕給多個電腦
    • 迅雷開啟多條線程一起下載
    • QQ同時和多個人一起視頻
    • 服務(wù)器同時處理多個客戶端請求

24.02_多線程(多線程并行和并發(fā)的區(qū)別)(了解)

  • 并行就是兩個任務(wù)同時運行,就是甲任務(wù)進(jìn)行的同時,乙任務(wù)也在進(jìn)行咸作。(需要多核CPU)
  • 并發(fā)是指兩個任務(wù)都請求運行锨阿,而處理器只能按受一個任務(wù),就把這兩個任務(wù)安排輪流進(jìn)行记罚,由于時間間隔較短墅诡,使人感覺兩個任務(wù)都在運行。
  • 比如我跟兩個網(wǎng)友聊天桐智,左手操作一個電腦跟甲聊末早,同時右手用另一臺電腦跟乙聊天,這就叫并行说庭。
  • 如果用一臺電腦我先給甲發(fā)個消息然磷,然后立刻再給乙發(fā)消息,然后再跟甲聊刊驴,再跟乙聊姿搜。這就叫并發(fā)。

24.03_多線程(Java程序運行原理和JVM的啟動是多線程的嗎)(了解)

  • A:Java程序運行原理
    • Java命令會啟動java虛擬機(jī)捆憎,啟動JVM舅柜,等于啟動了一個應(yīng)用程序,也就是啟動了一個進(jìn)程躲惰。該進(jìn)程會自動啟動一個 “主線程” 致份,然后主線程去調(diào)用某個類的 main 方法。
  • B:JVM的啟動是多線程的嗎
    • JVM啟動至少啟動了垃圾回收線程和主線程礁扮,所以是多線程的知举。

24.04_多線程(多線程程序?qū)崿F(xiàn)的方式1)(掌握)

  • 1.繼承Thread
    • 定義類繼承Thread

    • 重寫run方法

    • 把新線程要做的事寫在run方法中

    • 創(chuàng)建線程對象

    • 開啟新線程, 內(nèi)部會自動執(zhí)行run方法

      public class Demo2_Thread {
          /**
           * @param args
           */
          public static void main(String[] args) {
              MyThread mt = new MyThread();                           //4,創(chuàng)建自定義類的對象
              mt.start();                                             //5,開啟線程
              
              for(int i = 0; i < 3000; i++) {
                  System.out.println("bb");
              }
          }
      
      }
      class MyThread extends Thread {                                 //1,定義類繼承Thread
          public void run() {                                         //2,重寫run方法
              for(int i = 0; i < 3000; i++) {                         //3,將要執(zhí)行的代碼,寫在run方法中
                  System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
              }
          }
      }
      

24.05_多線程(多線程程序?qū)崿F(xiàn)的方式2)(掌握)

  • 2.實現(xiàn)Runnable
    • 定義類實現(xiàn)Runnable接口

    • 實現(xiàn)run方法

    • 把新線程要做的事寫在run方法中

    • 創(chuàng)建自定義的Runnable的子類對象

    • 創(chuàng)建Thread對象, 傳入Runnable

    • 調(diào)用start()開啟新線程, 內(nèi)部會自動調(diào)用Runnable的run()方法

      public class Demo3_Runnable {
              /**
               * @param args
               */
              public static void main(String[] args) {
                  MyRunnable mr = new MyRunnable();                       //4,創(chuàng)建自定義類對象
                  //Runnable target = new MyRunnable();
                  Thread t = new Thread(mr);                              //5,將其當(dāng)作參數(shù)傳遞給Thread的構(gòu)造函數(shù)
                  t.start();                                              //6,開啟線程
                  
      
              for(int i = 0; i < 3000; i++) {
                  System.out.println("bb");
              }
          }
      }
      
      class MyRunnable implements Runnable {                          //1,自定義類實現(xiàn)Runnable接口
          @Override
          public void run() {                                         //2,重寫run方法
              for(int i = 0; i < 3000; i++) {                         //3,將要執(zhí)行的代碼,寫在run方法中
                  System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
              }
          }
          
      }
      

24.06_多線程(實現(xiàn)Runnable的原理)(了解)

  • 查看源碼
    • 1,看Thread類的構(gòu)造函數(shù),傳遞了Runnable接口的引用
    • 2,通過init()方法找到傳遞的target給成員變量的target賦值
    • 3,查看run方法,發(fā)現(xiàn)run方法中有判斷,如果target不為null就會調(diào)用Runnable接口子類對象的run方法

24.07_多線程(兩種方式的區(qū)別)(掌握)

  • 查看源碼的區(qū)別:
    • a.繼承Thread : 由于子類重寫了Thread類的run(), 當(dāng)調(diào)用start()時, 直接找子類的run()方法
    • b.實現(xiàn)Runnable : 構(gòu)造函數(shù)中傳入了Runnable的引用, 成員變量記住了它, start()調(diào)用run()方法時內(nèi)部判斷成員變量Runnable的引用是否為空, 不為空編譯時看的是Runnable的run(),運行時執(zhí)行的是子類的run()方法
  • 繼承Thread
    • 好處是:可以直接使用Thread類中的方法,代碼簡單
    • 弊端是:如果已經(jīng)有了父類,就不能用這種方法
  • 實現(xiàn)Runnable接口
    • 好處是:即使自己定義的線程類有了父類也沒關(guān)系,因為有了父類也可以實現(xiàn)接口,而且接口是可以多實現(xiàn)的
    • 弊端是:不能直接使用Thread中的方法需要先獲取到線程對象后,才能得到Thread的方法,代碼復(fù)雜

24.08_多線程(匿名內(nèi)部類實現(xiàn)線程的兩種方式)(掌握)

  • 繼承Thread類

    new Thread() {                                                    //1,new 類(){}繼承這個類
          public void run() {                                         //2,重寫run方法
              for(int i = 0; i < 3000; i++) {                         //3,將要執(zhí)行的代碼,寫在run方法中
                  System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
              }
          }
      }.start();
    
  • 實現(xiàn)Runnable接口

    new Thread(new Runnable(){                                        //1,new 接口(){}實現(xiàn)這個接口
          public void run() {                                         //2,重寫run方法
              for(int i = 0; i < 3000; i++) {                         //3,將要執(zhí)行的代碼,寫在run方法中
                  System.out.println("bb");
              }
          }
      }).start(); 
    

24.09_多線程(獲取名字和設(shè)置名字)(掌握)

  • 1.獲取名字
    • 通過getName()方法獲取線程對象的名字
  • 2.設(shè)置名字
    • 通過構(gòu)造函數(shù)可以傳入String類型的名字
      new Thread("xxx") {
      public void run() {
      for(int i = 0; i < 1000; i++) {
      System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa");
      }
      }
      }.start();
    new Thread("yyy") {
        public void run() {
            for(int i = 0; i < 1000; i++) {
                System.out.println(this.getName() + "....bb");
            }
        }
    }.start(); 
  • 通過setName(String)方法可以設(shè)置線程對象的名字

    Thread t1 = new Thread() {
            public void run() {
                for(int i = 0; i < 1000; i++) {
                    System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa");
                }
            }
        };
        
    
    Thread t2 = new Thread() {
        public void run() {
            for(int i = 0; i < 1000; i++) {
                System.out.println(this.getName() + "....bb");
            }
        }
    };
    t1.setName("芙蓉姐姐");
    t2.setName("鳳姐");
    
    t1.start();
    t2.start();
    

24.10_多線程(獲取當(dāng)前線程的對象)(掌握)

  • Thread.currentThread(), 主線程也可以獲取

    new Thread(new Runnable() {
          public void run() {
              for(int i = 0; i < 1000; i++) {
                  System.out.println(Thread.currentThread().getName() + "...aaaaaaaaaaaaaaaaaaaaa");
              }
          }
      }).start();
    
    new Thread(new Runnable() {
      public void run() {
          for(int i = 0; i < 1000; i++) {
              System.out.println(Thread.currentThread().getName() + "...bb");
          }
      }
    }).start();
    Thread.currentThread().setName("我是主線程");                  //獲取主函數(shù)線程的引用,并改名字
    System.out.println(Thread.currentThread().getName());     //獲取主函數(shù)線程的引用,并獲取名字
    

24.11_多線程(休眠線程)(掌握)

  • Thread.sleep(毫秒,納秒), 控制當(dāng)前線程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000納秒 1000000000

    new Thread() {
              public void run() {
                  for(int i = 0; i < 10; i++) {
                      System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
                      try {
                          Thread.sleep(10);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }.start();
      
      new Thread() {
          public void run() {
              for(int i = 0; i < 10; i++) {
                  System.out.println(getName() + "...bb");
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }.start();
    

24.12_多線程(守護(hù)線程)(掌握)

  • setDaemon(), 設(shè)置一個線程為守護(hù)線程, 該線程不會單獨執(zhí)行, 當(dāng)其他非守護(hù)線程都執(zhí)行結(jié)束后, 自動退出

    Thread t1 = new Thread() {
          public void run() {
              for(int i = 0; i < 50; i++) {
                  System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      };
    
    Thread t2 = new Thread() {
      public void run() {
          for(int i = 0; i < 5; i++) {
              System.out.println(getName() + "...bb");
              try {
                  Thread.sleep(10);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
    };
    
    t1.setDaemon(true);                       //將t1設(shè)置為守護(hù)線程
    
    t1.start();
    t2.start();
    

24.13_多線程(加入線程)(掌握)

  • join(), 當(dāng)前線程暫停, 等待指定的線程執(zhí)行結(jié)束后, 當(dāng)前線程再繼續(xù)

  • join(int), 可以等待指定的毫秒之后繼續(xù)

    final Thread t1 = new Thread() {
          public void run() {
              for(int i = 0; i < 50; i++) {
                  System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      };
      
    
    Thread t2 = new Thread() {
      public void run() {
          for(int i = 0; i < 50; i++) {
              if(i == 2) {
                  try {
                      //t1.join();                        //插隊,加入
                      t1.join(30);                        //加入,有固定的時間,過了固定時間,繼續(xù)交替執(zhí)行
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      
                      e.printStackTrace();
                  }
              }
              System.out.println(getName() + "...bb");
          
          }
      }
    };
    
    t1.start();
    t2.start();
    

24.14_多線程(禮讓線程)(了解)

  • yield讓出cpu

24.15_多線程(設(shè)置線程的優(yōu)先級)(了解)

  • setPriority()設(shè)置線程的優(yōu)先級

24.16_多線程(同步代碼塊)(掌握)

  • 1.什么情況下需要同步
    • 當(dāng)多線程并發(fā), 有多段代碼同時執(zhí)行時, 我們希望某一段代碼執(zhí)行的過程中CPU不要切換到其他線程工作. 這時就需要同步.
    • 如果兩段代碼是同步的, 那么同一時間只能執(zhí)行一段, 在一段代碼沒執(zhí)行結(jié)束之前, 不會執(zhí)行另外一段代碼.
  • 2.同步代碼塊
    • 使用synchronized關(guān)鍵字加上一個鎖對象來定義一段代碼, 這就叫同步代碼塊

    • 多個同步代碼塊如果使用相同的鎖對象, 那么他們就是同步的

      class Printer {
              Demo d = new Demo();
              public static void print1() {
                  synchronized(d){                //鎖對象可以是任意對象,但是被鎖的代碼需要保證是同一把鎖,不能用匿名對象
                      System.out.print("黑");
                      System.out.print("馬");
                      System.out.print("程");
                      System.out.print("序");
                      System.out.print("員");
                      System.out.print("\r\n");
                  }
              }
      
          public static void print2() {   
              synchronized(d){    
                  System.out.print("傳");
                  System.out.print("智");
                  System.out.print("播");
                  System.out.print("客");
                  System.out.print("\r\n");
              }
          }
      }
      

24.17_多線程(同步方法)(掌握)

  • 使用synchronized關(guān)鍵字修飾一個方法, 該方法中所有的代碼都是同步的

    class Printer {
          public static void print1() {
              synchronized(Printer.class){  //鎖對象可以是任意對象,但是被鎖的代碼需要保證是同一把鎖,不能用匿名對象
                  System.out.print("黑");
                  System.out.print("馬");
                  System.out.print("程");
                  System.out.print("序");
                  System.out.print("員");
                  System.out.print("\r\n");
              }
          }
          /*
           * 非靜態(tài)同步函數(shù)的鎖是:this
           * 靜態(tài)的同步函數(shù)的鎖是:字節(jié)碼對象
           */
          public static synchronized void print2() {  
              System.out.print("傳");
              System.out.print("智");
              System.out.print("播");
              System.out.print("客");
              System.out.print("\r\n");
          }
      }
    

24.18_多線程(線程安全問題)(掌握)

  • 多線程并發(fā)操作同一數(shù)據(jù)時, 就有可能出現(xiàn)線程安全問題

  • 使用同步技術(shù)可以解決這種問題, 把操作數(shù)據(jù)的代碼進(jìn)行同步, 不要多個線程一起操作

    public class Demo2_Synchronized {
          /**
           * @param args
           * 需求:鐵路售票,一共100張,通過四個窗口賣完.
           */
          public static void main(String[] args) {
              TicketsSeller t1 = new TicketsSeller();
              TicketsSeller t2 = new TicketsSeller();
              TicketsSeller t3 = new TicketsSeller();
              TicketsSeller t4 = new TicketsSeller();
              
              t1.setName("窗口1");
              t2.setName("窗口2");
              t3.setName("窗口3");
              t4.setName("窗口4");
              t1.start();
              t2.start();
              t3.start();
              t4.start();
          }
      
      }
      
      class TicketsSeller extends Thread {
          private static int tickets = 100;
          static Object obj = new Object();
          public TicketsSeller() {
              super();
              
          }
          public TicketsSeller(String name) {
              super(name);
          }
          public void run() {
              while(true) {
                  synchronized(obj) {
                      if(tickets <= 0) 
                          break;
                      try {
                          Thread.sleep(10);//線程1睡,線程2睡,線程3睡,線程4睡
                      } catch (InterruptedException e) {
                          
                          e.printStackTrace();
                      }
                      System.out.println(getName() + "...這是第" + tickets-- + "號票");
                  }
              }
          }
      }
    

24.19_多線程(火車站賣票的例子用實現(xiàn)Runnable接口)(掌握)

24.20_多線程(死鎖)(了解)

  • 多線程同步的時候, 如果同步代碼嵌套, 使用相同鎖, 就有可能出現(xiàn)死鎖
    • 盡量不要嵌套使用

      private static String s1 = "筷子左";
          private static String s2 = "筷子右";
          public static void main(String[] args) {
              new Thread() {
                  public void run() {
                      while(true) {
                          synchronized(s1) {
                              System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
                              synchronized(s2) {
                                  System.out.println(getName() + "...拿到" + s2 + "開吃");
                              }
                          }
                      }
                  }
              }.start();
              
      
          new Thread() {
              public void run() {
                  while(true) {
                      synchronized(s2) {
                          System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
                          synchronized(s1) {
                              System.out.println(getName() + "...拿到" + s1 + "開吃");
                          }
                      }
                  }
              }
          }.start();
      }
      

24.21_多線程(以前的線程安全的類回顧)(掌握)

  • A:回顧以前說過的線程安全問題
    • 看源碼:Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
    • Vector是線程安全的,ArrayList是線程不安全的
    • StringBuffer是線程安全的,StringBuilder是線程不安全的
    • Hashtable是線程安全的,HashMap是線程不安全的
      24.22_多線程(總結(jié))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市太伊,隨后出現(xiàn)的幾起案子雇锡,更是在濱河造成了極大的恐慌,老刑警劉巖僚焦,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锰提,死亡現(xiàn)場離奇詭異,居然都是意外死亡芳悲,警方通過查閱死者的電腦和手機(jī)立肘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來名扛,“玉大人谅年,你說我怎么就攤上這事“谷停” “怎么了融蹂?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵旺订,是天一觀的道長。 經(jīng)常有香客問我超燃,道長区拳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任意乓,我火速辦了婚禮樱调,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘届良。我一直安慰自己笆凌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布伙窃。 她就那樣靜靜地躺著菩颖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪为障。 梳的紋絲不亂的頭發(fā)上晦闰,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機(jī)與錄音鳍怨,去河邊找鬼呻右。 笑死,一個胖子當(dāng)著我的面吹牛鞋喇,可吹牛的內(nèi)容都是我干的声滥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼侦香,長吁一口氣:“原來是場噩夢啊……” “哼落塑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起罐韩,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤憾赁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后散吵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體龙考,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年矾睦,在試婚紗的時候發(fā)現(xiàn)自己被綠了晦款。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡枚冗,死狀恐怖缓溅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赁温,我是刑警寧澤肛宋,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布州藕,位于F島的核電站,受9級特大地震影響酝陈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毁涉,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一沉帮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贫堰,春花似錦穆壕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至偎行,卻和暖如春川背,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛤袒。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工熄云, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妙真。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓缴允,卻偏偏與公主長得像,于是被迫代替她去往敵國和親珍德。 傳聞我的和親對象是個殘疾皇子练般,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359