<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      第14章 多線程基礎

      第14章 多線程基礎

      14.1 線程相關概念

      14.1.1 程序(program)

      是為完成特定任務,用某種語言編寫的一組指令的集合,即編寫的代碼。
      示例代碼(以Java為例):

      public class BallMove extends JFrame { 
          MyPanel mp = null;
          public static void main(String[] args) { 
              BallMove ballMove = new BallMove(); 
          }
          public BallMove() {
              mp = new MyPanel();
              this.add(mp);
              this.setSize(400, 300);
              this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              this.setVisible(true);
          }
      }
      

      14.1.2 進程

      • 運行中的程序,如啟動QQ、迅雷,操作系統會為其分配內存空間。
      • 是程序的一次執行過程,有產生、存在和消亡的動態過程 。

      14.1.3 線程

      • 由進程創建,是進程的實體,一個進程可擁有多個線程 。
      • 舉例:后續會將多線程加入“坦克大戰”游戲實踐。

      14.1.4 其他相關概念

      • 單線程:同一時刻僅允許執行一個線程。
      • 多線程:同一時刻可執行多個線程,如QQ同時開多個聊天窗口、迅雷同時下載多個文件 。
      • 并發:同一時刻多個任務交替執行,單CPU實現“貌似同時” 。
      • 并行:同一時刻多個任務真正同時執行,多核CPU可實現,Java支持。

      14.2 線程基本使用

      14.2.1 創建線程的兩種方式(Java)

      1. 繼承Thread:重寫run方法,定義線程執行邏輯。
      2. 實現Runnable接口:重寫run方法,解決Java單繼承局限,更靈活。

      image-20250826155116826

      14.2.2 應用案例1 - 繼承Thread

      package com.ming.threaduse;
      
      /**
       * @author 明
       * @version 1.0
       * 演示通過繼承Thread 類創建線程
       */
      public class Thread01 {
          public static void main(String[] args) throws InterruptedException {
              //創建Cat對象,可以當做線程使用
              Cat cat = new Cat();
      
              //讀源碼
              /*
                  (1)
                  public synchronized void start() {
                      start0();
                  }
                  (2)
                  //start0() 是本地方法,是JVM調用, 底層是c/c++實現
                  //真正實現多線程的效果, 是start0(), 而不是 run
                  private native void start0();
      
               */
      
      
              //cat.run();//run方法就是一個普通的方法, 沒有真正的啟動一個線程,就會把run方法執行完畢,才向下執行
              //說明: 當main線程啟動一個子線程 Thread-0, 主線程不會阻塞, 會繼續執行
              //這時 主線程和子線程是交替執行..
              cat.start();//啟動線程-> 最終會執行cat的run方法
      
              System.out.println("主線程繼續執行" +  Thread.currentThread().getName());
              for (int i = 0; i < 10; i++) {
                  System.out.println("主線程 i= " + i);
                  //讓主線程休眠
                  Thread.sleep(1000);
              }
          }
      }
      //說明
      //1.當一個類繼承了Thread類,該類就可以當作線程使用
      //2. 我們會重寫 run方法,寫上自己的業務代碼
      //3. run Thread 類 實現了 Runnable 接口的run方法
      /*
          @Override
          public void run() {
              if (target != null) {
                  target.run();
              }
          }
       */
      class Cat extends Thread{
      
          int times = 0;
          @Override
          public void run() {//重寫run方法,寫上自己的業務邏輯
      
      
              while (true) {
                  System.out.println("喵喵,我是小貓咪"+times++ + Thread.currentThread().getName());
      
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                     e.printStackTrace();
                  }
                  if(times==8){
                      break;
                  }
              }
          }
      }
      

      image-20250826155248300

      14.2.3 應用案例2 - 實現Runnable接口

      需求:每隔1秒在控制臺輸出“hi!”,輸出10次后退出。
      實現

      package com.ming.threaduse;
      
      import java.util.concurrent.Callable;
      import java.util.concurrent.FutureTask;
      
      /**
       * @author 明
       * @version 1.0
       * 通過實現接口Runnable 來開發線程
       */
      public class Thread02 {
          public static void main(String[] args) throws InterruptedException {
      //        Dog dog = new Dog();
      //        //dog.start(); 這里不能調用start
      //        //創建了Thread對象,把 dog對象(實現Runnable),放入Thread
      //        Thread thread = new Thread(dog);
      //        thread.start();
              Tiger tiger = new Tiger();
              ThreadProxy threadProxy = new ThreadProxy(tiger);
              threadProxy.start();
          }
      }
      
      class Animal {
      }
      
      class Tiger extends Animal implements Runnable {
      
          @Override
          public void run() {
              System.out.println("老虎嗷嗷叫....");
          }
      }
      
      //線程代理類 , 模擬了一個極簡的Thread類
      class ThreadProxy implements Runnable {//你可以把Proxy類當做 ThreadProxy
      
         private Runnable target = null;//屬性,類型是 Runnable
      
          @Override
          public void run() {
              if (target != null) {
                  target.run();//動態綁定(運行類型Tiger)
              }
          }
      
          public ThreadProxy(Runnable target) {
              this.target = target;
          }
      
          public void start() {
              start0();//這個方法時真正實現多線程方法
          }
      
          public void start0() {
              run();
          }
      }
      
      class Dog implements Runnable {
          int count = 0;
          @Override
          public void run() {
              while (true) {
                  System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
      
                  //休眠1秒
                  try {
                      Thread.sleep(1000);
                  }catch (Exception e){
                      e.printStackTrace();
                  }
                  if(count == 10){
                      break;
                  }
              }
          }
      }
      

      14..2.4線程使用應用案例-多線程執行

      package com.ming.threaduse;
      
      /**
       * @author 明
       * @version 1.0
       * main線程啟動兩個子線程
       */
      public class Thread03 {
          public static void main(String[] args) {
              T1 t1 = new T1();
              T2 t2 = new T2();
              Thread thread1 = new Thread(t1);
              Thread thread2 = new Thread(t2);
              thread1.start();
              thread2.start();
          }
      }
      class T1 implements Runnable{
          int count = 0;
          @Override
          public void run() {
              while (true) {
                  System.out.println("hello,world  " + (++count));
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  if (count == 10){
                      break;
                  }
              }
          }
      }
      
      class T2 implements Runnable{
          int count = 0;
          @Override
          public void run() {
              while (true) {
                  System.out.println("hi  " + (++count));
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  if (count == 5){
                      break;
                  }
              }
          }
      }
      

      image-20250826160519318

      image-20250826160538179

      14.3 繼承Thread vs 實現Runnable的區別

      1. 設計角度Thread類本身實現Runnable接口,本質無區別,但實現接口更靈活。
      2. 適用場景:實現Runnable適合多線程共享資源,且避免單繼承限制,推薦優先使用。
      3. 案例:售票系統
        模擬三個售票窗口售100張票,對比兩種方式,分析線程安全等問題(如資源競爭導致超賣,后續需同步機制解決 )。
      package com.ming.ticket;
      
      import com.hspedu.ticket.SellTicket;
      
      /**
       * @author 明
       * @version 1.0
       * 使用多線程,模擬三個窗口同時售票100張
       */
      public class SellTIcket {
          public static void main(String[] args) {
              //測試
      //        SellTicket01 sellTicket01 = new SellTicket01();
      //        SellTicket01 sellTicket02 = new SellTicket01();
      //        SellTicket01 sellTicket03 = new SellTicket01();
      //
      //        //這里我們會出現超賣..
      //        sellTicket01.start();//啟動售票線程
      //        sellTicket02.start();//啟動售票線程
      //        sellTicket03.start();//啟動售票線程
      
              System.out.println("===使用實現接口方式來售票=====");
              SellTicket02 sellTicket02 = new SellTicket02();
      
              new Thread(sellTicket02).start();
              new Thread(sellTicket02).start();
              new Thread(sellTicket02).start();
          }
      }
      //使用Thread方式
      class SellTicket01 extends Thread {
      
          private static int ticketNum = 100;//讓多個線程共享ticketNum
      
          @Override
          public void run() {
              while (true){
                  if (ticketNum <= 0){
                      System.out.println("售票結束~~");
                      break;
                  }
                  //休息50ms
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一張票"
                          + " 剩余票數=" + (--ticketNum));//1 - 0 - -1  - -2
              }
          }
      }
      class SellTicket02 implements  Runnable {
          private int ticketNum = 100;
      
          @Override
          public void run() {
              while (true) {
                  if (ticketNum <= 0) {
                      System.out.println("售票結束~~");
                      break;
                  }
                  //休息50ms
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一張票"
                          + " 剩余票數=" + (--ticketNum));//1 - 0 - -1  - -2
              }
          }
      }
      

      14.4 線程終止

      14.4.1 基本說明

      • 線程完成任務后自動退出。
      • 也可通過修改變量控制run方法退出(通知方式),不推薦強制終止(如stop方法已棄用 )。

      14.4.2 應用案例(ThreadExit.java

      需求:在main線程中停止子線程,通過標志位控制。
      示例邏輯:定義子線程類,用布爾變量標記是否繼續執行,main線程觸發標記改變終止子線程。

      package com.ming.exit_;
      
      /**
       * @author 明
       * @version 1.0
       */
      public class ThreadExit_ {
          public static void main(String[] args) throws InterruptedException {
              T t = new T();
              t.start();
      
              //如果希望main線程去控制t1 線程的終止, 必須可以修改 loop
              //讓t1 退出run方法,從而終止 t1線程 -> 通知方式
      
              //讓主線程休眠 10 秒,再通知 t1線程退出
              System.out.println("main線程休眠10s...");
              Thread.sleep(10*1000);
              t.setLoop(false);
          }
      }
      
      class T extends Thread {
          private int count = 0;
          //設置一個控制變量
          private boolean loop = true;
          @Override
          public void run() {
              while (loop) {
      
                  try {
                      Thread.sleep(50);// 讓當前線程休眠50ms
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("T 運行中...." + (++count));
              }
      
          }
      
          public void setLoop(boolean loop) {
              this.loop = loop;
          }
      }
      

      14.5 線程常用方法

      14.5.1 常用方法第一組

      • setName/getName:設置、獲取線程名稱。
      • start:啟動線程,JVM調用run方法。
      • run:線程執行體,直接調用是普通方法調用,非啟動新線程。
      • setPriority/getPriority:設置、獲取線程優先級(1 - 10,默認5 )。
      • sleep:讓當前線程休眠指定毫秒數,阻塞狀態。
      • interrupt:中斷線程(需配合處理InterruptedException )。

      14.5.2 注意事項

      • start才會真正啟動新線程,直接調run是普通方法調用。
      • 優先級僅為調度提示,不保證執行順序。
      • interrupt用于中斷阻塞線程,需合理處理中斷邏輯。

      14.5.3 應用案例(ThreadMethod01.java

      測試上述方法,如創建線程設置名稱、優先級,演示sleepinterrupt等用法。

      package com.ming.method;
      
      /**
       * @author 明
       * @version 1.0
       */
      public class ThreadMethod01 {
          public static void main(String[] args) throws InterruptedException {
              T t = new T();
              t.setName("小明");
              t.setPriority(Thread.MIN_PRIORITY);
              t.start();
      
              //主線程打印5個 hi ,然后就中斷 子線程的休眠
              for (int i = 0; i < 5; i++) {
                  Thread.sleep(1000);
                  System.out.println("hi " + i);
              }
              System.out.println(t.getName() + " 線程的優先級 =" + t.getPriority());//1
      
              t.interrupt();//當執行到這里,就會中斷 t線程的休眠.
          }
      }
      
      
      class T extends Thread { //自定義的線程類
          @Override
          public void run() {
              while (true) {
                  for (int i = 0; i < 100; i++) {
                      //Thread.currentThread().getName() 獲取當前線程的名稱
                      System.out.println(Thread.currentThread().getName() + "  吃包子~~~~" + i);
                  }
                  try {
                      System.out.println(Thread.currentThread().getName() + " 休眠中~~~");
                      Thread.sleep(20000);//20秒
                  } catch (InterruptedException e) {
                      //當該線程執行到一個interrupt 方法時,就會catch 一個 異常, 可以加入自己的業務代碼
                      //InterruptedException 是捕獲到一個中斷異常.
                      System.out.println(Thread.currentThread().getName() + "被 interrupt了");
                  }
              }
          }
      }
      

      14.5.4 常用方法第二組

      • yield:線程禮讓CPU,讓其他線程執行,禮讓時間不確定,不一定成功。
      • join:線程插隊,調用join的線程會阻塞,直到被調用線程執行完畢。
        • 案例:main線程創建子線程,按規則輸出內容,結合join控制執行順序 。

      image-20250827162121479

      14.5.5 應用案例(ThreadMethod02.java

      測試yieldjoin

      package com.ming.method;
      
      /**
       * @author 明
       * @version 1.0
       */
      public class ThreadMethod02 {
          public static void main(String[] args) throws InterruptedException {
              T2 t2 = new T2();
              t2.start();
              for (int i = 1; i <= 20; i++) {
                  Thread.sleep(1000);//休眠1秒
                  System.out.println("主線程 吃了 " + i +  " 包子");
                  if(i == 5){
                      System.out.println("主線程(小弟) 讓 子線程(老大) 先吃");
                      //join, 線程插隊
                      //t2.join();// 這里相當于讓t2 線程先執行完畢
                      Thread.yield();//禮讓,不一定成功..
                      System.out.println("線程(老大) 吃完了 主線程(小弟) 接著吃..");
                  }
              }
          }
      }
      
      class T2 extends Thread {
          @Override
          public void run() {
              for (int i = 1; i <= 20; i++) {
                  try {
                      Thread.sleep(1000);//休眠1秒
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("子線程(老大) 吃了 " + i +  " 包子");
              }
          }
      }
      

      (演示join使子線程優先執行完部分邏輯,體會線程協作 。)

      14.5.6 課堂練習

      package com.ming.method;
      
      /**
       * @author 明
       * @version 1.0
       */
      public class ThreadMethod0Exercise {
      
          public static void main(String[] args) throws InterruptedException {
              T3 t3 = new T3();
              for (int i = 1; i <= 10; i++) {
                  Thread.sleep(1000);//休眠1秒
                  System.out.println("hi " + i);
                  if(i == 5) {//說明主線程輸出了5次 hi
                      t3.start();//啟動子線程 輸出 hello...
                      t3.join();//立即將t3子線程,插入到main線程,讓t3先執行
                  }
                  Thread.sleep(1000);//輸出一次 hi, 讓main線程也休眠1s
              }
              System.out.println("主線程結束...");
          }
      }
      
      class T3 extends Thread {
          @Override
          public void run() {
              for (int i = 1; i <= 10; i++) {
                  try {
                      Thread.sleep(1000);//休眠1秒
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("hello " + i);
              }
          }
      }
      

      14.5.7 用戶線程和守護線程

      • 用戶線程:也叫工作線程,任務執行完或通過通知方式結束。
      • 守護線程:為工作線程服務,所有用戶線程結束后,守護線程自動結束,典型如垃圾回收機制 。
      • 應用案例ThreadMethod03.java,演示將線程設置為守護線程的操作。
      package com.ming.method;
      
      /**
       * @author 明
       * @version 1.0
       */
      public class ThreadMethod03 {
          public static void main(String[] args) throws InterruptedException {
              MyDaemom myDaemom = new MyDaemom();
              myDaemom.setDaemon(true);
              myDaemom.start();
              for( int i = 1; i <= 10; i++) {//main線程
                  System.out.println("寶強在辛苦的工作...");
                  Thread.sleep(1000);
              }
          }
      }
      class MyDaemom extends Thread {
          @Override
          public void run() {
              for(;;){
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("馬蓉和宋喆快樂聊天,哈哈哈~~~");
              }
          }
      }
      

      14.6 線程的生命周期

      14.6.1 線程狀態(JDK 中 Thread.State 枚舉表示)

      • NEW:線程尚未啟動。
      • RUNNABLE:線程在 Java 虛擬機中執行。
      • BLOCKED:線程被阻塞,等待監視器鎖。
      • WAITING:線程等待另一個線程執行特定動作。
      • TIMED_WAITING:線程等待另一個線程執行動作,且有指定等待時間。
      • TERMINATED:線程已退出 。

      image-20250827170352597

      package com.ming.state_;
      
      /**
       * @author 明
       * @version 1.0
       */
      public class ThreadState_ {
          public static void main(String[] args) throws InterruptedException {
              T t = new T();
              System.out.println(t.getName() + "狀態 "+t.getState());
              t.start();
              while(t.getState()!=Thread.State.TERMINATED){
                  System.out.println(t.getName() + "狀態 "+t.getState());
                  Thread.sleep(500);
              }
              System.out.println(t.getName() + "狀態 "+t.getState());
          }
      }
      class T extends Thread {
          @Override
          public void run() {
              for(int i = 1; i <= 10; i++) {
                  System.out.println("hi " + i);
                  try {
                      Thread.sleep(500);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      

      14.7 線程的同步

      14.7.1 問題引出(以窗口售票為例)

      多線程同時操作共享資源(如售票系統的余票),可能導致數據不一致(超賣、錯賣等問題 )。

      14.8 Synchronized 同步機制

      14.8.1 同步原理

      多線程編程中,敏感數據不允許被多個線程同時訪問,通過同步技術保證同一時刻最多一個線程訪問,確保數據完整性。也可理解為:一個線程操作內存時,其他線程無法訪問該內存地址,直到操作完成 。

      14.8.2 同步具體方法

      • 同步代碼塊
        synchronized (對象) { 
            // 需同步的代碼(獲得對象鎖后執行)
        }
        
      • 同步方法:在方法聲明前加 synchronized,表示整個方法為同步方法:
        public synchronized void method(String name) {
            // 需同步的代碼
        }
        
      • 理解類比:如同上廁所先關門(上鎖),完事再開門(解鎖),保證同一時間只有一個“使用者” 。
      • 應用:用 synchronized 解決售票問題,避免多線程競爭導致的錯誤。

      14.9 分析同步原理

      image-20250827170307576

      14.10 互斥鎖

      14.10.1 基本介紹

      • Java 引入對象互斥鎖,保證共享數據操作完整性,每個對象對應一個“互斥鎖”標記,確保同一時刻只有一個線程訪問該對象。
      • synchronized 關鍵字與互斥鎖關聯,修飾對象時,標記對象同一時刻只能被一個線程訪問。
      • 同步局限性:會降低程序執行效率;同步方法(非靜態)鎖對象默認是 this,也可為其他同一對象;靜態同步方法鎖對象是當前類的 class 對象 。

      14.10.2使用互斥鎖來解決售票問題

      package com.ming.syn;
      
      /**
       * @author 明
       * @version 1.0
       * 使用多線程,模擬三個窗口同時售票100張
       */
      public class SellTIcket {
          public static void main(String[] args) {
              //測試
      //        SellTicket01 sellTicket01 = new SellTicket01();
      //        SellTicket01 sellTicket02 = new SellTicket01();
      //        SellTicket01 sellTicket03 = new SellTicket01();
      //
      //        //這里我們會出現超賣..
      //        sellTicket01.start();//啟動售票線程
      //        sellTicket02.start();//啟動售票線程
      //        sellTicket03.start();//啟動售票線程
      
      //        System.out.println("===使用實現接口方式來售票=====");
      //        SellTicket02 sellTicket02 = new SellTicket02();
      //
      //        new Thread(sellTicket02).start();
      //        new Thread(sellTicket02).start();
      //        new Thread(sellTicket02).start();
      
              SellTicket03 sellTicket03 = new SellTicket03();
              new Thread(sellTicket03).start();
              new Thread(sellTicket03).start();
              new Thread(sellTicket03).start();
          }
      }
      
      //實現接口方式,使用synchronized實現線程同步
      class SellTicket03 implements  Runnable {
          private int ticketNum = 100;
          private boolean loop = true;
          private Object lock = new Object();
      
          //同步方法(靜態的)的鎖為當前類本身
          //解讀
          //1. public synchronized static void m1() {} 鎖是加在 SellTicket03.class
          //2. 如果在靜態方法中,實現一個同步代碼塊.
          /*
              synchronized (SellTicket03.class) {
                  System.out.println("m2");
              }
           */
          public synchronized static void m1() {
      
          }
         public  static void m2() {
              synchronized (SellTicket03.class) {
                  System.out.println("m2");
              }
          }
      
          //說明
          //1. public synchronized void sell() {} 就是一個同步方法
          //2. 這時鎖在 this對象
          //3. 也可以在代碼塊上寫 synchronize ,同步代碼塊, 互斥鎖還是在this對象
              public  void sell(){//同步方法,在同一時刻,只能有一個線程執行run方法
                  synchronized (lock) {
                      if (ticketNum <= 0) {
                          System.out.println("售票結束~~");
                          loop = false;
                          return;
                      }
                      //休息50ms
                      try {
                          Thread.sleep(10);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
      
                      System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一張票"
                              + " 剩余票數=" + (--ticketNum));//1 - 0 - -1  - -2
                  }
      
          }
      
          @Override
          public  void run() {
              while (loop) {
                  sell();
              }
          }
      }
      
      
      //使用Thread方式
      class SellTicket01 extends Thread {
      
          private static int ticketNum = 100;//讓多個線程共享ticketNum
      
          @Override
          public void run() {
              while (true){
                  if (ticketNum <= 0){
                      System.out.println("售票結束~~");
                      break;
                  }
                  //休息50ms
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一張票"
                          + " 剩余票數=" + (--ticketNum));//1 - 0 - -1  - -2
              }
          }
      }
      class SellTicket02 implements  Runnable {
          private int ticketNum = 100;
      
          @Override
          public void run() {
              while (true) {
                  if (ticketNum <= 0) {
                      System.out.println("售票結束~~");
                      break;
                  }
                  //休息50ms
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一張票"
                          + " 剩余票數=" + (--ticketNum));//1 - 0 - -1  - -2
              }
          }
      }
      

      14.10.3 注意事項和細節

      • 同步方法未用 static 修飾,默認鎖對象是 this;用 static 修飾,默認鎖對象是 當前類.class
      • 實現步驟:分析上鎖代碼 → 選擇同步代碼塊或同步方法 → 確保多個線程鎖對象相同 。

      14.11 線程的死鎖

      14.11.1 基本介紹

      多個線程互相占用對方所需的鎖資源,且都不釋放,導致程序“卡死”,編程需避免 。

      14.11.2 應用案例(生活場景)

      媽媽要求“完成作業才讓玩手機”,小明要求“讓玩手機才完成作業”,雙方互不相讓,形成死鎖 。

      14.11.3 應用案例(代碼)

      DeadLock_.java,演示代碼中線程因互相等待鎖資源導致死鎖的場景 。

      package com.ming.syn;
      
      /**
       * @author 明
       * @version 1.0
       */
      public class DeadLock_ {
          public static void main(String[] args) {
              DeadLockDemo a = new DeadLockDemo(true);
              DeadLockDemo b = new DeadLockDemo(false);
              a.setName("a線程");
              b.setName("b線程");
              a.start();
              b.start();
          }
      }
      //線程
      class DeadLockDemo extends Thread {
          static Object o1 = new Object();// 保證多線程,共享一個對象,這里使用static
          static Object o2 = new Object();
          boolean flag;
      
          public DeadLockDemo(boolean flag) {//構造器
              this.flag = flag;
          }
      
          @Override
          public void run() {
      
              //下面業務邏輯的分析
              //1. 如果flag 為 T, 線程A 就會先得到/持有 o1 對象鎖, 然后嘗試去獲取 o2 對象鎖
              //2. 如果線程A 得不到 o2 對象鎖,就會Blocked
              //3. 如果flag 為 F, 線程B 就會先得到/持有 o2 對象鎖, 然后嘗試去獲取 o1 對象鎖
              //4. 如果線程B 得不到 o1 對象鎖,就會Blocked
              if (flag) {
                  synchronized (o1) {//對象互斥鎖, 下面就是同步代碼
                      System.out.println(Thread.currentThread().getName() + " 進入1");
                      synchronized (o2) { // 這里獲得li對象的監視權
                          System.out.println(Thread.currentThread().getName() + " 進入2");
                      }
      
                  }
              } else {
                  synchronized (o2) {
                      System.out.println(Thread.currentThread().getName() + " 進入3");
                      synchronized (o1) { // 這里獲得li對象的監視權
                          System.out.println(Thread.currentThread().getName() + " 進入4");
                      }
                  }
              }
          }
      }
      

      14.12 釋放鎖

      14.12.1 會釋放鎖的操作

      • 同步方法/代碼塊執行結束(如“上完廁所出來” )。
      • 同步代碼塊/方法中遇到 breakreturn(如“沒正常完事,但因特殊情況退出” )。
      • 同步代碼塊/方法中出現未處理的 ErrorException,導致異常結束(如“忘帶紙,被迫出來” )。
      • 同步代碼塊/方法中執行 wait() 方法,當前線程暫停并釋放鎖(如“覺得需要醞釀,出來等會再進去” )。

      14.12.2 不會釋放鎖的操作

      • 線程執行同步代碼塊/方法時,調用 Thread.sleep()Thread.yield() 暫停執行(如“上廁所時在坑位上瞇一會” ,不釋放鎖 )。
      • 其他線程調用當前線程的 suspend() 方法掛起線程(suspend()resume() 方法已不推薦使用,易導致死鎖等問題 )。

      14.13 本章作業

      1. Homework01.java(5分鐘)

      • 需求:main 方法啟動兩個線程,第一個線程循環隨機打印 100 以內整數,直到第二個線程從鍵盤讀取到“Q”命令停止。
      public class Homework01 {
          public static void main(String[] args) {
              A a = new A();
              B b = new B(a);//一定要注意.
              a.start();
              b.start();
          }
      }
      
      //創建A線程類
      class A extends Thread {
          private boolean loop = true;
      
          @Override
          public void run() {
              //輸出1-100數字
              while (loop) {
                  System.out.println((int)(Math.random() * 100 + 1));
                  //休眠
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              System.out.println("a線程退出...");
      
          }
      
          public void setLoop(boolean loop) {//可以修改loop變量
              this.loop = loop;
          }
      }
      
      //直到第2個線程從鍵盤讀取了“Q”命令
      class B extends Thread {
          private A a;
          private Scanner scanner = new Scanner(System.in);
      
          public B(A a) {//構造器中,直接傳入A類對象
              this.a = a;
          }
      
          @Override
          public void run() {
              while (true) {
                  //接收到用戶的輸入
                  System.out.println("請輸入你指令(Q)表示退出:");
                  char key = scanner.next().toUpperCase().charAt(0);
                  if(key == 'Q') {
                      //以通知的方式結束a線程
                      a.setLoop(false);
                      System.out.println("b線程退出.");
                      break;
                  }
              }
          }
      }
      

      2. Homework02.java(5分鐘)

      • 需求:模擬 2 個用戶從同一銀行卡取錢(總額 10000),每次取 1000,余額不足則無法取款,需避免超取(解決線程同步問題 )。

      (作業需結合多線程、同步機制等知識實現,重點練習線程協作、資源競爭處理 。)

      package com.ming.homework;
      
      
      /**
       * @author 明
       * @version 1.0
       */
      public class Homework02 {
          public static void main(String[] args) {
              Account account = new Account();
              Thread t1 = new Thread(account);
              Thread t2 = new Thread(account);
              t1.setName("小明");
              t2.setName("張三");
              t1.start();
              t2.start();
          }
      }
      
      //編程取款的線程
      //1.因為這里涉及到多個線程共享資源,所以我們使用實現Runnable方式
      //2. 每次取出 1000
      class Account implements Runnable {
          private int balance = 10000;
          private boolean isRunning = true;
          private void drawMoney() {
              //解讀
              //1. 這里使用 synchronized 實現了線程同步
              //2. 當多個線程執行到這里時,就會去爭奪 this對象鎖
              //3. 哪個線程爭奪到(獲取)this對象鎖,就執行 synchronized 代碼塊, 執行完后,會釋放this對象鎖
              //4. 爭奪不到this對象鎖,就blocked ,準備繼續爭奪
              //5. this對象鎖是非公平鎖.
              synchronized (this) {
                  if (balance  -1000 <= 0) {
                      System.out.println("余額不足,不能取款了");
                      isRunning = false;
                  } else{
                      balance = balance - 1000;
                      System.out.println(Thread.currentThread().getName() + "正在取錢,取完后還剩"+balance);
                  }
              }
          }
      
          @Override
          public void run() {
              while (isRunning) {
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  drawMoney();
              }
          }
      }
      
      posted @ 2025-08-27 17:06  *珍惜當下*  閱讀(27)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品福利中文字幕| 日韩乱码人妻无码中文字幕视频 | 亚洲熟女国产熟女二区三区 | 亚洲AV成人片在线观看| 国产一区二区三区18禁| 国内精品一区二区不卡| 无码h黄肉动漫在线观看| 免费av深夜在线观看| 中文字幕人妻日韩精品| 乌海市| 国产美女裸身网站免费观看视频| 国产尤物精品自在拍视频首页| 大陆熟妇丰满多毛xxxx| 久久国产免费观看精品3| 亚洲色成人一区二区三区| 精品日韩人妻中文字幕| 国内少妇人妻偷人精品视频| 国产资源精品中文字幕| 国产一区二区不卡91| 亚洲丰满老熟女激情av| 久久综合精品国产一区二区三区无| 四虎永久精品免费视频| 精品一区二区三区蜜桃麻豆| 亚洲a人片在线观看网址| 乱人伦中文字幕成人网站在线 | 日本亚洲色大成网站www久久| 国内自拍av在线免费| 日本一区二区精品色超碰| 精品中文人妻在线不卡| 尤溪县| 国产99在线 | 欧美| 亚洲精品久久久久国色天香| 人人入人人爱| 国产精品国产精品国产精品| 么公的好大好硬好深好爽视频| 免费观看全黄做爰大片| 国产一区日韩二区三区| 色琪琪丁香婷婷综合久久| 国产精品国产精品偷麻豆| 一区二区亚洲人妻精品| 波多野结衣一区二区三区高清av|