<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java中如何保證線程順序執(zhí)行

          共 12700字,需瀏覽 26分鐘

           ·

          2021-05-16 12:05

                                              點擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達(dá)

            作者 |  六層樓

          來源 |  urlify.cn/IVz6vm

          76套java從入門到精通實戰(zhàn)課程分享

          只要了解過多線程,我們就知道線程開始的順序跟執(zhí)行的順序是不一樣的。如果只是創(chuàng)建三個線程然后執(zhí)行,最后的執(zhí)行順序是不可預(yù)期的。這是因為在創(chuàng)建完線程之后,線程執(zhí)行的開始時間取決于CPU何時分配時間片,線程可以看成是相對于的主線程的一個異步操作。

          public class FIFOThreadExample {
              public synchronized static void foo(String name) {
                  System.out.print(name);
              }

              public static void main(String[] args) {
                  Thread thread1 = new Thread(() -> foo("A"));
                  Thread thread2 = new Thread(() -> foo("B"));
                  Thread thread3 = new Thread(() -> foo("C"));
                  thread1.start();
                  thread2.start();
                  thread3.start();
              }
          }

          輸出結(jié)果:ACB/ABC/CBA...

          那么我們該如何保證線程的順序執(zhí)行呢?

          如何保證線程的順序執(zhí)行?

          1. 使用Thread.join()實現(xiàn)

          Thread.join()的作用是讓父線程等待子線程結(jié)束之后才能繼續(xù)運行。以上述例子為例,main()方法所在的線程是父線程,在其中我們創(chuàng)建了3個子線程A,B,C,子線程的執(zhí)行相對父線程是異步的,不能保證順序性。而對子線程使用Thread.join()方法之后就可以讓父線程等待子線程運行結(jié)束后,再開始執(zhí)行父線程,這樣子線程執(zhí)行被強(qiáng)行變成了同步的,我們用Thread.join()方法就能保證線程執(zhí)行的順序性。

          public class FIFOThreadExample {
              
              public static void foo(String name) {
                  System.out.print(name);
              }

              public static void main(String[] args) throws InterruptedException{
                  Thread thread1 = new Thread(() -> foo("A"));
                  Thread thread2 = new Thread(() -> foo("B"));
                  Thread thread3 = new Thread(() -> foo("C"));
                  thread1.start();
                  thread1.join();
                  thread2.start();
                  thread2.join();
                  thread3.start();
              }
          }

          輸出結(jié)果:ABC

          2. 使用單線程線程池來實現(xiàn)

          另一種保證線程順序執(zhí)行的方法是使用一個單線程的線程池,這種線程池中只有一個線程,相應(yīng)的,內(nèi)部的線程會按加入的順序來執(zhí)行。

          import java.util.concurrent.ExecutorService;
          import java.util.concurrent.Executors;
          public class FIFOThreadExample {

              public static void foo(String name) {
                  System.out.print(name);
              }

              public static void main(String[] args) throws InterruptedException{
                  Thread thread1 = new Thread(() -> foo("A"));
                  Thread thread2 = new Thread(() -> foo("B"));
                  Thread thread3 = new Thread(() -> foo("C"));
                  ExecutorService executor = Executors.newSingleThreadExecutor();
                  executor.submit(thread1);
                  executor.submit(thread2);
                  executor.submit(thread3);
                  executor.shutdown();
              }
          }

          輸出結(jié)果:ABC

          3. 使用volatile關(guān)鍵字修飾的信號量實現(xiàn)

          上面兩種的思路都是讓保證線程的執(zhí)行順序,讓線程按一定的順序執(zhí)行。這里介紹第三種思路,那就是線程可以無序運行,但是執(zhí)行結(jié)果按順序執(zhí)行。
          你應(yīng)該可以想到,三個線程都被創(chuàng)建并start(),這時候三個線程隨時都可能執(zhí)行run()方法。因此為了保證run()執(zhí)行的順序性,我們肯定需要一個信號量來讓線程知道在任意時刻能不能執(zhí)行邏輯代碼。
          另外,因為三個線程是獨立的,這個信號量的變化肯定需要對其他線程透明,因此volatile關(guān)鍵字也是必須要的。

          public class TicketExample2 {

              //信號量
              static volatile int ticket = 1;
              //線程休眠時間
              public final static int SLEEP_TIME = 1;

              public static void foo(int name){
                  //因為線程的執(zhí)行順序是不可預(yù)期的,因此需要每個線程自旋
                  while (true) {
                      if (ticket == name) {
                          try {
                              Thread.sleep(SLEEP_TIME);
                              //每個線程循環(huán)打印3次
                              for (int i = 0; i < 3; i++) {
                                  System.out.println(name + " " + i);
                              }

                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          //信號量變更
                          ticket = name%3+1;
                          return;

                      }
                  }
              }
              public static void main(String[] args) throws InterruptedException {
                  Thread thread1 = new Thread(() -> foo(1));
                  Thread thread2 = new Thread(() -> foo(2));
                  Thread thread3 = new Thread(() -> foo(3));
                  thread1.start();
                  thread2.start();
                  thread3.start();
              }
          }

          執(zhí)行結(jié)果:
          1 0
          1 1
          1 2
          2 0
          2 1
          2 2
          3 0
          3 1
          3 2

          4. 使用Lock和信號量實現(xiàn)

          此種方法的思想跟第三種方法是一樣的,都是不考慮線程執(zhí)行的順序而是考慮用一些方法控制線程執(zhí)行業(yè)務(wù)邏輯的順序。這里我們同樣用一個原子類型信號量ticket,當(dāng)然你可以不用原子類型,這里我只是為了保證自增操作的線程安全。然后我們用了一個可重入鎖ReentrantLock。用來給方法加鎖,當(dāng)一個線程拿到鎖并且標(biāo)識位正確的時候開始執(zhí)行業(yè)務(wù)邏輯,執(zhí)行完畢后喚醒下一個線程。
          這里我們不需要使用while進(jìn)行自旋操作了,因為Lock可以讓我們喚醒指定的線程,所以改成if就可以實現(xiàn)順序的執(zhí)行。

          public class TicketExample3 {
              //信號量
              AtomicInteger ticket = new AtomicInteger(1);
              public Lock lock = new ReentrantLock();
              private Condition condition1 = lock.newCondition();
              private Condition condition2 = lock.newCondition();
              private Condition condition3 = lock.newCondition();
              private Condition[] conditions = {condition1, condition2, condition3};

              public void foo(int name) {
                  try {
                      lock.lock();
                      //因為線程的執(zhí)行順序是不可預(yù)期的,因此需要每個線程自旋
                      System.out.println("線程" + name + " 開始執(zhí)行");
                      if(ticket.get() != name) {
                          try {
                              System.out.println("當(dāng)前標(biāo)識位為" + ticket.get() + ",線程" + name + " 開始等待");
                              //開始等待被喚醒
                              conditions[name - 1].await();
                              System.out.println("線程" + name + " 被喚醒");
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      System.out.println(name);
                      ticket.getAndIncrement();
                      if (ticket.get() > 3) {
                          ticket.set(1);
                      }
                      //執(zhí)行完畢,喚醒下一次。1喚醒2,2喚醒3
                      conditions[name % 3].signal();
                  } finally {
                      //一定要釋放鎖
                      lock.unlock();
                  }

              }

              public static void main(String[] args) throws InterruptedException {
                  TicketExample3 example = new TicketExample3();
                  Thread t1 = new Thread(() -> {
                      example.foo(1);
                  });
                  Thread t2 = new Thread(() -> {
                      example.foo(2);
                  });
                  Thread t3 = new Thread(() -> {
                      example.foo(3);
                  });
                  t1.start();
                  t2.start();
                  t3.start();
              }
          }

          輸出結(jié)果:
          線程2 開始執(zhí)行
          當(dāng)前標(biāo)識位為1,線程2 開始等待
          線程1 開始執(zhí)行
          1
          線程3 開始執(zhí)行
          當(dāng)前標(biāo)識位為2,線程3 開始等待
          線程2 被喚醒
          2
          線程3 被喚醒
          3

          上述的執(zhí)行結(jié)果并非唯一,但可以保證打印的順序一定是123這樣的順序。





          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點贊支持下哈 

          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费欧美性爱 | 俺去俺来也在线www色官网 | 在线观看中文字幕一区 | 亚洲性爱自拍 | 日韩精品电影 |