主頁 > 後端開發 > 第8章 多執行緒

第8章 多執行緒

2023-04-23 07:33:46 後端開發

8.1 執行緒簡介

1 、多任務

  • 現實生活中多件事一起作,

  • 在程式中是指在一個系統中可以同時進行多個行程,即有多個單獨運行的任務,每一個任務對應一個行程,

  • 每一個行程都有一段專用的記憶體區域,即使是多次啟動同一段程式產生不同的行程也是如此,

2、多執行緒

  • Java 給多執行緒編程提供了內置的支持, 一條執行緒指的是行程中一個單一順序的控制流,一個行程中可以并發多個執行緒,每條執行緒并行執行不同的任務,

  • 多執行緒是多任務的一種特別的形式,但多執行緒使用了更小的資源開銷,

  • 多執行緒能滿足程式員撰寫高效率的程式來達到充分利用 CPU 的目的,

  • 主執行緒和子執行緒交替執行

3、程式、行程、執行緒

  • 程式

    • 程式是指令和資料的有序集合,其本身沒有任何運行的含義,是一個靜態的概念,

  • 行程

    • 一個應用程式(1個行程是一個軟體)

    • 是執行程式的一次執行程序,是一個流動的概念,是系統資源分配的單位,

    • 一個行程包括由作業系統分配的記憶體空間,包含一個或多個執行緒,一個行程一直運行,直到所有的非守護執行緒都結束運行后才能結束,

  • 執行緒

    • 一個行程中的執行場景/執行單元,

    • 執行緒是CPU調度和執行的單位,

    • 一個執行緒不能獨立的存在,它必須是行程的一部分,

  • 注意:

    • 一個行程可以有多個執行緒

    • 執行緒是獨立的執行路徑,

    • 在程式與運行時,即使沒有自己創建執行緒,后臺也會有多個執行緒,如主執行緒,gc執行緒

    • main()稱為主執行緒,為系統的入口,用于執行整個程式;

    • 在一個行程中,如果開辟了多個行程,執行緒的運行由調度器安排調度,調度器是與作業系統緊密相關的,先后順序是不能認為的干預的,

    • 對于同一份資源操作,會存在資源搶奪的問題,需要加入并發控制,

    • 執行緒會帶來額外的開銷,CPU的調度時間、并發控制開銷,

    • 每一個執行緒在自己的作業記憶體互動,記憶體控制不當會造成資料不一致,

8.2 執行緒創建

  • 三種創建方式:

    • Thread class 繼承Thread類

    • Runnable介面 實作Runnable介面

    • Callable介面 實作Callable介面

8.2.1 Thread類

  1. 創建一個新的執行執行緒的方法,將一個宣告為Thread的子類,

  2. 自定義執行緒類繼承Thread類

  3. 重寫run()方法,撰寫執行緒執行體

  4. 創建執行緒物件,呼叫start()方法啟動執行緒,

  5. 注意:

    • 執行緒開啟不一定立即執行,由CPU調度執行

    • 啟動執行緒:子類物件.start()

    • 不建議使用,為了避免OOP單繼承的局限性

  6. 實體

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/20
    * JavaSE
    * 創建多執行緒 繼承Thread類
    */
    public class TestThread1 extends Thread{
       @Override
       public void run() {
           for (int i = 0; i < 20; i++) {
               System.out.println("我在看代碼-----"+i);
          }
      }
    ?
       public static void main(String[] args) {
           //main 主執行緒
    ?
           //創建一個執行緒物件
           TestThread1 testThread1 = new TestThread1();
    ?
           //呼叫start()方法開啟執行緒
           testThread1.start();
           for (int i = 0; i < 20; i++) {
               System.out.println("我在學習多執行緒---"+i);
          }
      }
    }
    ?
    //多條執行路徑,主執行緒和子執行緒并行交替執行
    ?
    執行結果:
    我在學習多執行緒---0
    我在看代碼-----0
    我在學習多執行緒---1
    我在看代碼-----1
    我在學習多執行緒---2
    我在看代碼-----2
    我在看代碼-----3
    我在學習多執行緒---3
    我在看代碼-----4
    我在看代碼-----5
    我在學習多執行緒---4
    我在學習多執行緒---5
    我在看代碼-----6
    我在學習多執行緒---6
    我在看代碼-----7
    我在學習多執行緒---7
    我在看代碼-----8
    我在學習多執行緒---8
    我在看代碼-----9
    我在學習多執行緒---9
    我在看代碼-----10
    我在學習多執行緒---10
    我在學習多執行緒---11
    我在學習多執行緒---12
    我在學習多執行緒---13
    我在學習多執行緒---14
    我在學習多執行緒---15
    我在學習多執行緒---16
    我在學習多執行緒---17
    我在學習多執行緒---18
    我在學習多執行緒---19
    我在看代碼-----11
    我在看代碼-----12
    我在看代碼-----13
    我在看代碼-----14
    我在看代碼-----15
    我在看代碼-----16
    我在看代碼-----17
    我在看代碼-----18
    我在看代碼-----19

8.2.2 Runnable

  1. 創建一個執行緒是宣告實作類Runnable介面

  2. 定義MyRunable類實作Runnable介面

  3. 實作run()方法,撰寫執行緒執行體

  4. 創建執行緒物件,呼叫start()方法啟動執行緒

  5. 注意:

    • 啟動執行緒:傳入目標物件+Thread物件.start()

    • 推薦使用,避免單繼承局限性,靈活方便,方便同一個物件被多個執行緒使用,

  6. 實體:

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/20
    * JavaSE
    * 實作Runnable介面執行緒
    */
    public class TestThread2 implements Runnable{
       
       //重寫方法
       @Override
       public void run() {
           for (int i = 0; i < 20; i++) {
               System.out.println("我在看代碼-----"+i);
          }
      }
    ?
       public static void main(String[] args) {
           //main 主執行緒
    ?
           //創建一個執行緒物件
           TestThread2 testThread2 = new TestThread2();
           //代理,通過執行緒物件來開啟執行緒
           Thread thread = new Thread(testThread2);
           thread.start();
           for (int i = 0; i < 20; i++) {
               System.out.println("我在學習多執行緒---"+i);
          }
      }
    }
    ?
    ?
    ?
    執行結果:
    我在學習多執行緒---0
    我在學習多執行緒---1
    我在看代碼-----0
    我在學習多執行緒---2
    我在看代碼-----1
    我在學習多執行緒---3
    我在看代碼-----2
    我在學習多執行緒---4
    我在學習多執行緒---5
    我在學習多執行緒---6
    我在看代碼-----3
    我在學習多執行緒---7
    我在學習多執行緒---8
    我在學習多執行緒---9
    我在學習多執行緒---10
    我在學習多執行緒---11
    我在學習多執行緒---12
    我在學習多執行緒---13
    我在學習多執行緒---14
    我在學習多執行緒---15
    我在學習多執行緒---16
    我在學習多執行緒---17
    我在學習多執行緒---18
    我在看代碼-----4
    我在學習多執行緒---19
    我在看代碼-----5
    我在看代碼-----6
    我在看代碼-----7
    我在看代碼-----8
    我在看代碼-----9
    我在看代碼-----10
    我在看代碼-----11
    我在看代碼-----12
    我在看代碼-----13
    我在看代碼-----14
    我在看代碼-----15
    我在看代碼-----16
    我在看代碼-----17
    我在看代碼-----18
    我在看代碼-----19
  7. 案例:

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/20
    * JavaSE
    *多個執行緒同時操作同一個物件
    * 買火車票的例子
    */
    public class TestThread4 implements Runnable{
    ?
       private int a = 10;
    ?
       @Override
       public void run() {
           while (true){
               if (a<=0){
                   break;
              }
               System.out.println(Thread.currentThread().getName()+"拿到了"+a--+"票");
          }
    ?
      }
    ?
       public static void main(String[] args) {
           TestThread4 b = new TestThread4();
           new Thread(b,"小明").start();
           new Thread(b,"老師").start();
           new Thread(b,"黃牛").start();
           new Thread(b,"警察").start();
      }
    }
    ?
    執行結果:
    小明拿到了9票
    老師拿到了8票
    警察拿到了7票
    黃牛拿到了10票
    警察拿到了4票
    警察拿到了2票
    警察拿到了1票
    老師拿到了5票
    小明拿到了6票
    黃牛拿到了3票
    ?
  8. 實體:龜兔賽跑

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/20
    * JavaSE
    * 龜兔賽跑
    * 首先來個賽道距離,然后要離距離越來越近
    * 判斷比賽是否結束
    * 列印出勝利者
    * 龜兔賽跑開始
    * 模擬兔子睡覺
    * 烏龜贏得比賽
    */
    public class TestThread3 implements Runnable{
    ?
       //勝利者
       private static String winner;
    ?
       @Override
       public void run() {
    ?
           //跑步開始
           for (int i = 0; i <= 100; i++) {
    ?
               //模擬兔子睡覺
               if(Thread.currentThread().getName().equals("兔子") && i%10 == 0){
                   try {
                       Thread.sleep(1);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
    ?
              }
    ?
               //判斷比賽是否結束
               boolean flag = gameOver(i);
               //如果比賽結束了,就停止程式
               if(flag){
                   break;
              }
               System.out.println(Thread.currentThread().getName()+"---->跑了"+i+"步");
          }
    ?
      }
    ?
       //判斷是否完成比賽
       private boolean gameOver(int steps){
           //判斷是否有勝利者
           if(winner!=null){//已經存在勝利者
               return true;
          }{
               if (steps >= 100){
                   winner = Thread.currentThread().getName();
                   System.out.println("winner ="+winner);
                   return true;
              }
          }
           return false;
      }
    ?
       public static void main(String[] args) {
    ?
           //設定賽道
           TestThread3 race = new TestThread3();
    ?
           new Thread(race,"兔子").start();
           new Thread(race,"烏龜").start();
    ?
      }
    }
    ?
    ?
    運行結果:
    烏龜---->跑了0步
    烏龜---->跑了1步
    烏龜---->跑了2步
    烏龜---->跑了3步
    烏龜---->跑了4步
    烏龜---->跑了5步
    烏龜---->跑了6步
    烏龜---->跑了7步
    烏龜---->跑了8步
    烏龜---->跑了9步
    烏龜---->跑了10步
    烏龜---->跑了11步
    烏龜---->跑了12步
    烏龜---->跑了13步
    烏龜---->跑了14步
    烏龜---->跑了15步
    烏龜---->跑了16步
    烏龜---->跑了17步
    烏龜---->跑了18步
    烏龜---->跑了19步
    烏龜---->跑了20步
    烏龜---->跑了21步
    烏龜---->跑了22步
    烏龜---->跑了23步
    烏龜---->跑了24步
    烏龜---->跑了25步
    烏龜---->跑了26步
    烏龜---->跑了27步
    烏龜---->跑了28步
    烏龜---->跑了29步
    烏龜---->跑了30步
    烏龜---->跑了31步
    烏龜---->跑了32步
    烏龜---->跑了33步
    烏龜---->跑了34步
    烏龜---->跑了35步
    烏龜---->跑了36步
    烏龜---->跑了37步
    烏龜---->跑了38步
    烏龜---->跑了39步
    烏龜---->跑了40步
    烏龜---->跑了41步
    烏龜---->跑了42步
    烏龜---->跑了43步
    烏龜---->跑了44步
    烏龜---->跑了45步
    烏龜---->跑了46步
    烏龜---->跑了47步
    烏龜---->跑了48步
    烏龜---->跑了49步
    烏龜---->跑了50步
    烏龜---->跑了51步
    烏龜---->跑了52步
    烏龜---->跑了53步
    烏龜---->跑了54步
    烏龜---->跑了55步
    烏龜---->跑了56步
    烏龜---->跑了57步
    兔子---->跑了0步
    烏龜---->跑了58步
    兔子---->跑了1步
    兔子---->跑了2步
    兔子---->跑了3步
    兔子---->跑了4步
    兔子---->跑了5步
    兔子---->跑了6步
    兔子---->跑了7步
    兔子---->跑了8步
    兔子---->跑了9步
    烏龜---->跑了59步
    烏龜---->跑了60步
    烏龜---->跑了61步
    烏龜---->跑了62步
    烏龜---->跑了63步
    烏龜---->跑了64步
    烏龜---->跑了65步
    烏龜---->跑了66步
    烏龜---->跑了67步
    烏龜---->跑了68步
    烏龜---->跑了69步
    烏龜---->跑了70步
    烏龜---->跑了71步
    烏龜---->跑了72步
    烏龜---->跑了73步
    烏龜---->跑了74步
    烏龜---->跑了75步
    烏龜---->跑了76步
    烏龜---->跑了77步
    烏龜---->跑了78步
    烏龜---->跑了79步
    烏龜---->跑了80步
    烏龜---->跑了81步
    烏龜---->跑了82步
    烏龜---->跑了83步
    烏龜---->跑了84步
    烏龜---->跑了85步
    烏龜---->跑了86步
    烏龜---->跑了87步
    烏龜---->跑了88步
    烏龜---->跑了89步
    烏龜---->跑了90步
    烏龜---->跑了91步
    烏龜---->跑了92步
    烏龜---->跑了93步
    烏龜---->跑了94步
    烏龜---->跑了95步
    烏龜---->跑了96步
    烏龜---->跑了97步
    烏龜---->跑了98步
    兔子---->跑了10步
    兔子---->跑了11步
    兔子---->跑了12步
    兔子---->跑了13步
    兔子---->跑了14步
    兔子---->跑了15步
    兔子---->跑了16步
    兔子---->跑了17步
    兔子---->跑了18步
    兔子---->跑了19步
    烏龜---->跑了99步
    winner =烏龜

8.2.3 實作Callable介面

  1. 實作Callable介面,需要回傳值型別

  2. 重寫call方法,需要拋出例外

  3. 創建目標物件

  4. 創建執行服務:ExecutorService ser =Executors.newFixedThreadPool(1);

  5. 提交執行:Futre < Boolean>result1 =ser.submit(t1);

  6. 獲取結果:boolean r1 = result1.get()

  7. 關閉服務:ser.shutdownNow();

  8. 實體

      public class CallableTest {
         public static void main(String[] args) throws ExecutionException, InterruptedException,TimeoutException{
             //創建一個執行緒池
             ExecutorService executor = Executors.newCachedThreadPool();
             Future<String> future = executor.submit(()-> {
                     TimeUnit.SECONDS.sleep(5);
                     return "CallableTest";
            });
             System.out.println(future.get());
             executor.shutdown();
        }
    }
    ?

8.3 lamda運算式和靜態代理模式

8.3.1 lamda運算式

  1. 希臘字母表中排序第十一位的字母,英文名為Lambda

  2. 避免匿名內部類定義過多,

  3. 代理模式其實就是通過一個類去代替另一個類去做一些操作,

  4. 其實質屬于函式式編程的概念

    (params) ->expression[運算式]
    (params) ->statement[陳述句]
    (params) ->{statements}
  5. 為什么要用lambda運算式

    • 避免匿名內部內定義過多

    • 代碼更加簡潔

    • 去掉沒有意義的代碼,只留下核心代碼

  6. Functional Interface(函式式介面)是lamda運算式的關鍵,

    • 函式式介面:任何介面,如果只包含唯一一個抽象的方法,那他就是函式式介面

  7. 實體:

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    * 推導lambda運算式
    */
    public class TestThread5 {
    ?
       //3靜態內部類,實作類
       static class Like2 implements Ilike{
           @Override
           public void lambda(){
               System.out.println("i like lambda2");
          }
      }
    ?
       public static void main(String[] args) {
           Ilike like = new Like();
           like.lambda();
    ?
           like=new Like2();
           like.lambda();
    ?
    ?
           //4區域內部類
           class Like3 implements Ilike{
               @Override
               public void lambda(){
                   System.out.println("i like lambda3");
              }
          }
           like=new Like3();
           like.lambda();
    ?
           //5匿名內部類
           like = new  Ilike() {
               @Override
               public void lambda() {
                   System.out.println("i like lambda4");
              }
          };
           like.lambda();
    ?
           //6用lambda簡化
           like = ()->{
               System.out.println("i like lambda5");
          };
           like.lambda();
    ?
      }
    }
    ?
    //1定義一個函式式介面
    interface Ilike{
       void lambda();
    }
    //2實作類
    class Like implements Ilike{
       @Override
       public void lambda(){
           System.out.println("i like lambda");
      }
    }
    ?
  8. 總結:

    • lambda運算式只能有一行代碼的情況下才能簡化成一行,如果有多行,就要用代碼塊包裹

    • 前提是函式式介面,只有一個方法,

    • 多個引數型別也可以去掉引數型別,要去掉都要去掉,

8.3.2 靜態代理模式

  1. 為其他物件提供一個代理以控制對這個物件的訪問,

  2. 主要解決:在直接訪問物件時帶來的問題,比如說:要訪問的物件在遠程的機器上,在面向物件系統中,有些物件由于某些原因(比如物件創建開銷很大,或者某些操作需要安全控制,或者需要行程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此物件時加上一個對此物件的訪問層,

  3. 代理模式的元素是:共同介面、代理物件、目標物件,

  4. 代理模式的行為:由代理物件執行目標物件的方法、由代理物件擴展目標物件的方法,

  5. 代理模式的宏觀特性:對客戶端只暴露出介面,不暴露它以下的架構,

  6. 好處多多:中間隔離了一層,更加符合開閉原則

  7. 流程圖

    img

  8. 實體:

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    */
    public class TeatThread6 {
    ?
       public static void main(String[] args) {
           WeddingCompany weddingCompany = new WeddingCompany(new You());
           weddingCompany.HappyMarry();
      }
    }
    ?
    interface Marry{
       void HappyMarry();
    }
    ?
    //真實角色
    class You implements Marry{
       @Override
       public void HappyMarry() {
           System.out.println("小明要結婚了");
      }
    }
    ?
    //代理角色,幫助結婚
    class WeddingCompany implements Marry{
    ?
       //代理真實角色
       private Marry target;
       //建構式
       public WeddingCompany(Marry target) {
           this.target = target;
      }
    ?
       @Override
       public void HappyMarry() {
    ?
           before();
           this.target.HappyMarry();//這就是真實物件
           after();
      }
    ?
       private void after() {
           System.out.println("結婚之后");
      }
    ?
       private void before() {
           System.out.println("結婚前");
      }
    }
    ?
  9. 總結:

    • 真實物件和代理物件都要事先同一個介面

    • 代理物件要代理真實角色,

    • 好處:

      • 代理物件可以做真實物件做不了的事情

      • 真實物件專注于自己的事情

8.4 執行緒狀態

  • 執行緒五大狀態

img

img

  • 執行緒方法

    方法說明
    setPriority(int newPriority) 更改現成的優先級
    static void sleep(long millis) 在指定的毫秒數內讓當前正在執行的執行緒休眠
    void join() 等待執行緒終止
    static void yield() 暫停當前正在執行的執行緒物件,并執行其他的執行緒
    void interrupt() 中斷執行緒,不要用這個方法
    boolean isAliven() 測驗執行緒是否處于活動狀態

8.4.1 停止執行緒

  1. 不推薦使用JDK提供的stop()、destory()方法,

  2. 推薦讓執行緒自己停下來

  3. 建議使用一個標志未進行終止變數當flag=false,則終止執行緒運行,

  4. 測驗停止執行緒

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    * 測驗停止執行緒
    * 注意死回圈
    */
    public class TestStop implements Runnable{
    ?
       //1.設定一個標識位
       private boolean flag = true;
    ?
       //重寫方法
       @Override
       public void run() {
           int i = 0;
           while (flag){
               System.out.println("執行緒正在運行"+i++);
          }
      }
    ?
       //2設定一個公開的方法,停止執行緒,轉換標志位
       public void stop(){
           this.flag = false;
      }
    ?
       public static void main(String[] args) {
    ?
           TestStop testStop = new TestStop();
           new Thread(testStop).start();
    ?
           for (int i = 0; i < 1000; i++) {
               System.out.println("main"+i);
               if(i == 900){
                   //呼叫stop方法,切換標志位,讓執行緒停止
                   testStop.stop();
                   System.out.println("執行緒該停止了");
              }
          }
      }
    }

8.4.2 執行緒休眠

  1. sleep(時間)指定當前執行緒阻塞的毫秒數;

  2. sleep存在例外InterruptedExcepiton;

  3. sleep時間到達后執行緒進入就緒狀態;

  4. sleep可以模擬網路延遲,倒計時等

  5. 每一個物件都有一個鎖,sleep不會釋放鎖

  6. 倒計時實體

    package Demo032;
    ?
    import java.text.SimpleDateFormat;
    import java.util.Date;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    */
    public class TestSleep2 {
       public static void main(String[] args) throws InterruptedException {
           tenDwon();
    ?
       //模擬倒計時
       public static void tenDwon() throws InterruptedException {
           int num = 10;
    ?
           while (true){
               Thread.sleep(1000);
               System.out.println(num--);
               if (num<=0){
                   break;
              }
          }
      }
    }
    ?
  7. 獲取當前時間

    package Demo032;
    ?
    import java.text.SimpleDateFormat;
    import java.util.Date;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    */
    public class TestSleep2 {
       public static void main(String[] args){
           //獲取系統當前時間
           Date startTime = new Date(System.currentTimeMillis());
           while (true) {
               try {
                   Thread.sleep(1000);
                   //更新系統時間
                   System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                   startTime = new Date(System.currentTimeMillis());
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
    ?
      }
    }
    ?

8.4.3 執行緒禮讓

  1. 禮讓執行緒,讓當前正在執行的執行緒暫停,但不阻塞

  2. 將執行緒從運行狀態轉為就緒狀態

  3. 讓CPU重新調度,禮讓不一定成功

  4. 實體

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    * 禮讓執行緒
    */
    public class TestYield {
    ?
       public static void main(String[] args) {
           MyYield myYield = new MyYield();
           new Thread(myYield,"a").start();
           new Thread(myYield,"b").start();
    ?
      }
    }
    ?
    class MyYield implements Runnable{
       @Override
       public void run() {
           System.out.println(Thread.currentThread().getName()+"執行緒開始執行");
           Thread.yield();//禮讓
           System.out.println(Thread.currentThread().getName()+"執行緒停止執行");
      }
    }

8.4.4 Join(插隊)

  1. Join合并執行緒,待此執行緒執行完成后,在執行其他的執行緒,其他的執行緒阻塞

  2. 可以想象為插隊

  3. 實體

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    * 插隊
    */
    public class TestJoin implements Runnable{
       @Override
       public void run() {
           for (int i = 0; i < 1000; i++) {
               System.out.println("執行緒VIP來了"+i);
          }
      }
    ?
       public static void main(String[] args) throws InterruptedException {
           
           //啟動執行緒
           TestJoin testJoin = new TestJoin();
           Thread thread =new Thread(testJoin);
           thread.start();
           
           //主執行緒
           for (int i = 0; i < 500; i++) {
               if(i == 200){
                   thread.join();//插隊
              }
               System.out.println("main"+i);
          }
      }
    }
    ?

8.4.5 執行緒狀態觀測

  • 執行緒狀態

  1. new:尚未啟動的執行緒處于此狀態,

  2. RUNNABLE:在JAVA虛擬機中執行的執行緒處于此狀態,

  3. BLOCKED:被阻塞等待監視器鎖定的執行緒處于此狀態,

  4. WAITING:正在等待另一個執行緒執行特定動作的執行緒處于此狀態,

  5. TIMED_WAITING:正在等待另一個執行緒執行動作達到指定等待時間的執行緒處于此狀態,

  6. TERMINATED:已退出的執行緒處于此狀態,

  • 一個執行緒可以在給定的時間處于一個狀態,這些狀態是不反映任何作業系統執行緒狀態的虛擬機狀態,

  • 實體

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    */
    public class TestState {
    ?
       public static void main(String[] args) throws InterruptedException {
           Thread thread = new Thread(()->{
               for (int i = 0; i < 5; i++) {
                   try {
                       Thread.sleep(1000);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
              }
               System.out.println("////");
          });
           
           //觀察狀態
           Thread.State state= thread.getState();
           System.out.println(state);//new
    ?
           //觀察啟動后
           thread.start();//啟動執行緒
           state = thread.getState();
           System.out.println(state);//run
    ?
           while (state != Thread.State.TERMINATED){//只要執行緒不終止,就一直輸出狀態
               Thread.sleep(100);
               state = thread.getState();//跟新執行緒狀態
               System.out.println(state);
          }
    ?
      }
    }
    ?
    ?
    運行結果
    NEW
    RUNNABLE
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    ////
    TERMINATED

8.4.6 執行緒優先級

  1. Java提供一個執行緒調度器來監控程式中啟動后進入就緒狀態的所有執行緒,執行緒調度器按照優先級決定應該調度哪一個執行緒來執行,

  2. 執行緒的優先級用數字表示,范圍從1~10.

    • Thread.MIN_PRIORITY = 1;

    • Thread.MAX_PRIORITY = 10;

    • Thread.NORM_PRIORITY =5;

  3. 使用以下方式改變或者獲取優先級

    • getPriority().setPriority(int xxx)

  4. 實體

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    * 測驗執行緒優先級
    */
    public class TestPriority {
    ?
       public static void main(String[] args) {
           System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
    ?
           MyPriority myPriority = new MyPriority();
           Thread t1 = new Thread(myPriority);
           Thread t2 = new Thread(myPriority);
           Thread t3 = new Thread(myPriority);
           Thread t4 = new Thread(myPriority);
           Thread t5 = new Thread(myPriority);
           Thread t6 = new Thread(myPriority);
    ?
           //先設定優先級
           t1.start();
    ?
           t2.setPriority(1);
           t2.start();
    ?
           t3.setPriority(4);
           t3.start();
    ?
           t4.setPriority(Thread.MAX_PRIORITY);
           t4.start();
    ?
           t5.setPriority(8);
           t5.start();
    ?
           t6.setPriority(3);
           t6.start();
      }
    ?
    }
    class MyPriority implements Runnable{
    ?
       @Override
       public void run() {
           System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
      }
    }
    ?

8.4.7 守護執行緒

  1. 執行緒分為用戶執行緒和守護執行緒

  2. 虛擬機必須確保用戶執行緒執行完畢

  3. 虛擬機不用等待守護執行緒執行完畢

  4. 實體

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/21
    * JavaSE
    * 測驗守護執行緒
    */
    public class TestDaemon {
       public static void main(String[] args) {
           God god = new God();
           You1 you = new You1();
           Thread thread = new Thread(god);
           thread.setDaemon(true);
    ?
           thread.start();
           new Thread(you).start();
      }
    ?
    }
    ?
    //上帝
    class God implements Runnable{
       @Override
       public void run() {
           while (true){
               System.out.println("上帝保佑著你");
          }
      }
    }
    ?
    ?
    //你
    class You1  implements Runnable{
       @Override
       public void run() {
           for (int i = 0; i < 365000; i++) {
               System.out.println("你一生都開心的活著");
          }
           System.out.println("====goodbye!word!=======");
      }
    }
    ?

8.5 執行緒同步

8.5.1 并發

  1. 并發:同一個物件被多個執行緒同時操作,

  2. 所謂并發編程是指在一臺處理器上 “同時” 處理多個任務,并發是在同一物體上的多個事件,多個事件在同一時間間隔發生,

  3. 并發編程,從程式設計的角度來說,是希望通過某些機制讓計算機可以在一個時間段內,執行多個任務,從計算機 CPU 硬體層面來說,是一個或多個物理 CPU 在多個程式之間多路復用,提高對計算機資源的利用率,從調度演算法角度來說,當任務數量多于 CPU 的核數時,并發編程能夠通過作業系統的任務調度演算法,實作多個任務一起執行,

  4. 并發編程有三大特性:

    • 原子性;

    • 可見性;

    • 有序性,

8.5.2 執行緒同步

  1. 執行緒有自己的私有資料,比如堆疊和暫存器,同時與其它執行緒共享相同的虛擬記憶體和全域變數等資源, 在一般情況下,創建一個執行緒是不能提高程式的執行效率的,所以要創建多個執行緒,但是當多個執行緒同時讀寫同一份共享資源的時候,會引起沖突,例如在多個執行緒同時對同一個記憶體地址進行寫入,由于CPU時間調度上的問題,寫入資料會被多次的覆寫,所以就要使執行緒同步,這時候就需要引入執行緒同步機制使各個執行緒排隊一個一個的對共享資源進行操作,而不是同時進行,

  2. 簡單的說就是,在多執行緒編程里面,一些資料不允許被多個執行緒同時訪問,此時就使用同步訪問技術,保證資料在任何時刻,最多有一個執行緒訪問,以保證資料的完整性,

  3. 處理多執行緒問題時,多執行緒訪問同一個物件,并且某些執行緒還想修改這個物件,這個時候我們就需要執行緒同步,

  4. 執行緒同步其實就是一種等待機制,多個需要同時訪問此物件的執行緒進入這個物件的等待池形成對列,等待前面執行緒使用完畢,下一個執行緒再使用,

  5. 由于同一行程的多個執行緒共享一塊存盤空間,在方便的同時,也帶來了訪問沖突,為了保證資料在方法中被訪問時的正確性,再訪問時加入鎖機制,當一個執行緒獲得物件的排他鎖,多占資源,其他執行緒必須等待,使用后釋放鎖即可,

    • 會損失性能

    • 優先級倒置

  6. 執行緒不安全案例

    package Demo032;
    ?
    /**
    * @Author: H-YONG-8
    * @DATA: 2023/4/22
    * JavaSE
    */
    public class UnsafeBuyTicket {
    ?
       public static void main(String[] args) {
           BuyTicket station = new BuyTicket();
           new Thread(station,"小明").start();
           new Thread(station,"黃牛").start();
           new Thread(station,"老師").start();
           new Thread(station,"教師").start();
    ?
      }
    }
    ?
    class BuyTicket implements Runnable{
    ?
       //票
       private int tikeNums = 10;
       boolean flag = true;
       @Override
       public void run() {
           //買票
           while (flag){
               try {
                   buy();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
    ?
      }
    ?
       private void buy() throws InterruptedException {
           //判斷是否有票
           if(tikeNums<=0){
               flag = false;
               return;
          }
           //模擬延時
           Thread.sleep(100);
    ?
           //買票
           System.out.println(Thread.currentThread().getName()+"拿到"+tikeNums--);
      }
    }
    ?
    ?
    執行結果:
    教師拿到10
    小明拿到7
    老師拿到8
    黃牛拿到9
    老師拿到6
    黃牛拿到4
    小明拿到5
    教師拿到6
    教師拿到2
    老師拿到3
    小明拿到1
    黃牛拿到0
    教師拿到-1
    老師拿到-2
  7. 同步方法

    • 由于我們可以通過private關鍵字來保證資料物件只能被方法訪問,所以我們只需要針對方法提出一套機制,這套機制就是syncyhronized關鍵字,包括兩種用法:同步方法、塊

      img

  8. 同步的弊端:

    • 方法里面需要修改的內容才需要鎖,鎖太多,浪費資源

  9. 同步塊

    • 同步塊:syncyhronized(obj){}

    • obj稱為同步監視器

      • obj可以是任何物件,但是推薦使用共享資源作為同步監視器,

      • 同步方法中無需指定同步監視器,因為同步方法的同步監視器就是this,就是這個物件本身,或者是class

    • 同步監視器的執行程序

      • 第一個執行緒訪問,鎖定同步監視器,執行其中的代碼

      • 第二個執行緒訪問,發現同步監視器被鎖定,無法訪問

      • 第一個執行緒訪問完畢,解鎖同步監視器

      • 第二個執行緒訪問完畢,發現同步監視器沒有鎖,然后鎖定并訪問,

8.5.3 佇列和鎖

1、佇列

  • 佇列是一個先進先出的抽象資料結構,可類比于生活中的排隊場景,通常情況下,佇列有陣列和鏈表兩種實作方式,

  • 采用鏈表實作的佇列,沒有個數限制,插入元素時直接接在鏈表的尾部,取出元素時直接從鏈表的頭部取出即可,

  • 采用陣列實作的佇列,通常是回圈陣列,受限于陣列的大小,存在天然的個數上限,插入和取出元素時,必須采用佇列頭部指標和佇列尾部指標進行佇列滿和佇列空的判斷,

  • Java 定義了佇列的基本操作,介面型別為 java.util.Queue,介面定義如下所示,Queue 定義了兩套佇列操作方法:

    • add、remove、element 操作失敗拋出例外;

    • offer 操作失敗回傳 false 或拋出例外,poll、peek 操作失敗回傳 null;

2、鎖

  • 同步操作的實作,需要給物件關聯一個互斥體,這個互斥體就可以叫做鎖,

  • 死鎖

    • 執行緒之間相互等著對方釋放資源,而自己的資源又不釋放給別人,這種情況就是死鎖,所以,只要其中一執行緒釋放了資源,死鎖就會被解除,

    • 實體

      package Demo032;
      ?
      /**
      * @Author: H-YONG-8
      * @DATA: 2023/4/22
      * JavaSE
      * 死鎖
      */
      public class DeadLock {
         public static void main(String[] args) {
             Makeup g1 = new Makeup(0,"小明");
             Makeup g2 = new Makeup(1,"小紅");
      ?
             g1.start();
             g2.start();
      ?
        }
      }
      ?
      //口紅
      class Lipstick{
      ?
      }
      ?
      //鏡子
      class Mirror{
      ?
      }
      class Makeup extends Thread{
      ?
         //需要的資源
         static  Lipstick lipstick = new Lipstick();
         static Mirror mirror = new Mirror();
      ?
         int chose;
         String girlname;
      ?
         Makeup(int chose,String girlname){
             this.chose = chose;
             this.girlname = girlname;
            }
      ?
      ?
      ?
      ?
      ?
         @Override
         public void run() {
             try {
                 makeup();
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
      ?
         //化妝
         private void makeup() throws InterruptedException {
             if (chose == 0) {
                 synchronized (lipstick){//獲得口號的鎖
                     System.out.println(this.girlname+"獲得口號的鎖");
                     Thread.sleep(1000);
                }
                 synchronized (mirror){
                     System.out.println(this.girlname+ "獲得鏡子的鎖");
                }
            }else {
                 synchronized (mirror){//獲得口號的鎖
                     System.out.println(this.girlname+"獲得鏡子的鎖");
                     Thread.sleep(2000);
                }
                 synchronized (lipstick){
                     System.out.println(this.girlname+ "獲得口號的鎖");
                }
            }
        }
      }
      ?
      ?
    • 產生死鎖的四個必要條件

      • 互斥條件:一個資源只能被一個行程使用

      • 請求與保持條件:一個行程因請求資源而阻塞時,對以獲得的資源不放手

      • 不剝奪條件:行程以獲得的資源,在未使用完前,不能強行剝奪

      • 回圈等待條件:若干行程之間形成一個頭尾相接的回圈等待資源關系,

  • 重入鎖

    • 重入鎖指的是,一個執行緒在擁有了當前資源的鎖之后,可以再次拿到該鎖而不被阻塞,在后面會講到synchronized的重入鎖原理,

  • 自旋鎖

    • 自旋鎖指的是,執行緒在沒有獲得鎖時,不是被直接掛起,而是執行一個慷訓圈(自旋),默認是回圈10次,

    • 自旋鎖的目的也就是為了減少執行緒被掛起的幾率,因為執行緒的掛起和喚醒也都是耗資源的操作,

    • 如果鎖被另一個執行緒占用的時間比較長,即使自旋了之后當前執行緒還是會被掛起,慷訓圈就會變成浪費系統資源的操作,反而降低整體性能,所以,自旋鎖是不適應鎖占用時間長的并發情況的,

  • 自適應自旋鎖

    • 自適應自旋鎖是對自鎖鎖的一種優化,當一個執行緒自旋后成功獲得了鎖,那么下次自旋的次數就會增加,因為虛擬機認為,既然上次自旋期間成功拿到了鎖,那么后面的自旋會有很大幾率拿到鎖,相反,如果對于某個鎖,很少有自旋能夠成功獲得的,那么后面就會減少自旋次數,甚至省略掉自旋程序,以免浪費處理器資源,這種鎖是默認開啟的,

  • 鎖消除

    • 鎖消除指的是,在編譯期間利用“逃逸分析技術”分析出那些不存在競爭卻加了鎖的代碼的鎖失效,這樣就減少了鎖的請求與釋放操作,因為鎖的請求與釋放都會消耗系統資源,

3、兩者關系

  • 佇列和鎖在一起解決執行緒安全性

8.5.4 Lock(鎖)

  • 顯示加鎖,可以知道位置

img

在這里插入圖片描述

  • 代碼實體:

    package dead;
    ?
    import java.util.concurrent.locks.ReentrantLock;
    ?
    public class TestLock {
       public static void main(String[] args) {
           TestLock2 testLock2=new TestLock2();
           new Thread(testLock2).start();
           new Thread(testLock2).start();
           new Thread(testLock2).start();
    ?
      }
    }
    //定義Lock
    ?
    //買票的執行緒
    class TestLock2 implements Runnable{
       //定義Lock
       private final ReentrantLock lock= new ReentrantLock();
       int num=10;
       @Override
       public void run() {
           while (true){
               try {
                   lock.lock();
                   if (num>0){
                       try {
                           Thread.sleep(1000);
                      } catch (InterruptedException e) {
                           e.printStackTrace();
                      }
                       System.out.println(num--);
                  }
                   else {
                       break;
                  }
    ?
              } finally {
                   lock.unlock();
              }
    ?
    ?
          }
    ?
      }
    }
    ?

     

 

 

8.6 執行緒通信問題

在這里插入圖片描述

 

在這里插入圖片描述

img

img

img

  • 實體

    /**
    * 測驗:生產者消費者模型-->利用緩沖區解決:管程法
    */
    public class Demo33_ThreadPC {
       public static void main(String[] args) {
           SynContainer synContainer = new SynContainer();
           new Producer(synContainer).start();
           new Consumer(synContainer).start();
      }
    }
    ?
    //生產者
    class Producer extends Thread {
       //容緩沖區
       SynContainer container;
    ?
       public Producer(SynContainer container) {
           this.container = container;
      }
    ?
       //生產
       @Override
       public void run() {
           for (int i = 0; i < 100; i++) {
               container.push(new Product(i));
               System.out.println("生產了" + i + "件產品");
          }
      }
    }
    ?
    //消費者
    class Consumer extends Thread {
       //容緩沖區
       SynContainer container;
    ?
       public Consumer(SynContainer container) {
           this.container = container;
      }
    ?
       //消費
       @Override
       public void run() {
           for (int i = 0; i < 100; i++) {
               System.out.println("消費了-->" + container.pop().id + "件產品");
          }
      }
    }
    ?
    //產品
    class Product {
       int id;//產品編號
    ?
       public Product(int id) {
           this.id = id;
      }
    }
    ?
    //緩沖區
    class SynContainer {
       //需要一個容器大小
       Product[] products = new Product[10];
       //容器計數器
       int count = 0;
    ?
       //生產者放入產品
       public synchronized void push(Product product) {
           //如果容器滿了,需要等待消費者消費
           /*如果是if的話,假如消費者1消費了最后一個,這是index變成0此時釋放鎖被消費者2拿到而不是生產者拿到,這時消費者的wait是在if里所以它就直接去消費index-1下標越界,如果是while就會再去判斷一下index得值是不是變成0了*/
           while (count == products.length) {
               //通知消費者消費,等待生產
               try {
                   this.wait();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
           //如果沒有滿,需要丟入產品
           products[count] = product;
           count++;
           //通知消費者消費
           this.notifyAll();
      }
    ?
       //消費者消費產品
       public synchronized Product pop() {
           //判斷是否能消費
           while (count <= 0) {
               //等待生產者生產
               try {
                   this.wait();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
           //如果可以消費
           count--;
           Product product = products[count];
           //吃完了 通知生產者生產
           this.notifyAll();
           return product;
      }
    }
    ?

     

8.7 執行緒池

在這里插入圖片描述

//測驗執行緒池
public class Demo35_ThreadPool {
   public static void main(String[] args) {
       // 1. 創建服務,擦行間執行緒池
       // newFixedThreadPool(執行緒池大小)
       ExecutorService service = Executors.newFixedThreadPool(10);
       //執行
       service.execute(new MyThread());
       service.execute(new MyThread());
       service.execute(new MyThread());
       service.execute(new MyThread());
       service.execute(new MyThread());
       service.execute(new MyThread());
       //關閉連接
       service.shutdown();
  }
}
?
class MyThread implements Runnable {
   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
  }
}
?

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550856.html

標籤:其他

上一篇:影像邊緣檢測(Canny)

下一篇:返回列表

標籤雲
其他(157853) Python(38092) JavaScript(25381) Java(17985) C(15215) 區塊鏈(8256) C#(7972) AI(7469) 爪哇(7425) MySQL(7137) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4557) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2430) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1959) Web開發(1951) HtmlCss(1919) python-3.x(1918) 弹簧靴(1913) C++(1910) xml(1889) PostgreSQL(1872) .NETCore(1854) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 第8章 多執行緒

    8.1 執行緒簡介 1 、多任務 現實生活中多件事一起作。 在程式中是指在一個系統中可以同時進行多個行程,即有多個單獨運行的任務,每一個任務對應一個行程。 每一個行程都有一段專用的記憶體區域,即使是多次啟動同一段程式產生不同的行程也是如此。 2、多執行緒 Java 給多執行緒編程提供了內置的支持。 一條執行緒 ......

    uj5u.com 2023-04-23 07:33:46 more
  • 影像邊緣檢測(Canny)

    Canny檢測的流程 Canny檢測主要是用于邊緣檢測 1)使用高斯濾波器,以平滑影像,濾除噪聲。 2)計算影像中每個像素點的梯度強度和方向。 3)應用非極大值(Non-Maximum Suppression)抑制,以消除邊緣檢測帶來的雜散回應 4)應用雙閾值(Double-Threshold)檢測 ......

    uj5u.com 2023-04-23 07:33:41 more
  • 洛谷:P5716日份天數

    題目描述 輸入年份和月份,輸出這一年的這一月有多少天。需要考慮閏年。 輸入格式 輸入兩個正整數,分別表示年份 $y$ 和月數 $m$,以空格隔開。 輸出格式 輸出一行一個正整數,表示這個月有多少天。 樣例 #1 樣例輸入 #1 1926 8 樣例輸出 #1 31 樣例輸入 #2 2000 2 樣例輸 ......

    uj5u.com 2023-04-23 07:33:33 more
  • 【Visual Leak Detector】原始碼檔案概覽

    說明 使用 VLD 記憶體泄漏檢測工具輔助開發時整理的學習筆記。本篇對 VLD 原始碼包中的各檔案用途做個概述。同系列文章目錄可見 《記憶體泄漏檢測工具》目錄 1. 整體概覽 以 vld2.5.1 版本為例,下載原始碼 后,根目錄下一共 5 個檔案夾:.teamcity、lib、mfc_detect、set ......

    uj5u.com 2023-04-23 07:33:28 more
  • Java學習(1)

    一、Java的基礎語法 1.變數和資料型別 在Java中,變數是用來存盤資料的容器,可以存盤各種型別的資料。Java中的變數分為兩類:基本資料型別變數和參考資料型別變數。 (1)基本資料型別(Primitive Data Types) 整數型別:byte、short、int、long 浮點型別:fl ......

    uj5u.com 2023-04-23 07:33:23 more
  • python| 關于excel的檔案處理

    創建一個成績單檔案score.xlsx,將平時成績單.xlsx檔案中對應班級作業表中學號和姓名列的內容寫入到score.xlsx中,并添加成績列,每個學生的成績采用隨機生成的一個分數填寫進去,最后統計所有學生的平均成績計算出來后,寫入到score.xlsx的最后一行最后一列之后的單元格中去。預想的步 ......

    uj5u.com 2023-04-23 07:33:18 more
  • Django筆記二十八之資料庫查詢優化匯總

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十八之資料庫查詢優化匯總 這一篇筆記將從以下幾個方面來介紹 Django 在查詢程序中的一些優化操作,有一些是介紹如何獲取 Django 查詢轉化的 sql 陳述句,有一些是理解 QuerySet 是如何獲取資料的。 以下是本篇筆記目錄: ......

    uj5u.com 2023-04-23 07:33:12 more
  • 多執行緒(一)

    #一:什么是多執行緒 執行緒是作業系統能夠進行運算調度的最小單位;它被包含在行程之中,是行程中的實際運作單位。 多執行緒,是指從軟體或者硬體上實作多個執行緒并發執行的技術。具有多執行緒能力的計算機因有硬體支持而能夠在同一時間執行多于一個執行緒,進而提升整體處理性能。 簡單來說:執行緒是程式中一個單一的順序控制流程 ......

    uj5u.com 2023-04-23 07:33:06 more
  • Springboot 多實體負載均衡部署

    Springboot 多實體負載均衡部署 一、測驗代碼: 控制層測驗代碼: import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; @Controller @Re ......

    uj5u.com 2023-04-23 07:33:00 more
  • Go中回應式編程庫RxGo詳細介紹

    最近的專案用到了 RxGo ,因為之前從沒有接觸過,特意去學了學,特此記錄下。文章很多內容是復制了參考資料或者官方檔案。如果涉及侵權,請聯系洗掉,謝謝。 1、RxGo簡介 1.1 基礎介紹 RxGo是一個基于Go語言的回應式編程庫,它提供了一種簡單而強大的方式來處理異步事件流和資料流。RxGo的設計 ......

    uj5u.com 2023-04-23 07:32:39 more