- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Java中保證線(xiàn)程順序執行的操作代碼
只要了解過(guò)多線(xiàn)程,我們就知道線(xiàn)程開(kāi)始的順序跟執行的順序是不一樣的。如果只是創(chuàng )建三個(gè)線(xiàn)程然后執行,最后的執行順序是不可預期的。這是因為在創(chuàng )建完線(xiàn)程之后,線(xiàn)程執行的開(kāi)始時(shí)間取決于CPU何時(shí)分配時(shí)間片,線(xiàn)程可以看成是相對于的主線(xiàn)程的一個(gè)異步操作。
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(); } }
輸出結果:ACB/ABC/CBA...
那么我們該如何保證線(xiàn)程的順序執行呢?
Thread.join()
的作用是讓父線(xiàn)程等待子線(xiàn)程結束之后才能繼續運行。以上述例子為例,main()
方法所在的線(xiàn)程是父線(xiàn)程,在其中我們創(chuàng )建了3個(gè)子線(xiàn)程A,B,C,子線(xiàn)程的執行相對父線(xiàn)程是異步的,不能保證順序性。而對子線(xiàn)程使用Thread.join()
方法之后就可以讓父線(xiàn)程等待子線(xiàn)程運行結束后,再開(kāi)始執行父線(xiàn)程,這樣子線(xiàn)程執行被強行變成了同步的,我們用Thread.join()
方法就能保證線(xiàn)程執行的順序性。
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(); } }
輸出結果:ABC
另一種保證線(xiàn)程順序執行的方法是使用一個(gè)單線(xiàn)程的線(xiàn)程池,這種線(xiàn)程池中只有一個(gè)線(xiàn)程,相應的,內部的線(xiàn)程會(huì )按加入的順序來(lái)執行。
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(); } }
輸出結果:ABC
上面兩種的思路都是讓保證線(xiàn)程的執行順序,讓線(xiàn)程按一定的順序執行。這里介紹第三種思路,那就是線(xiàn)程可以無(wú)序運行,但是執行結果按順序執行。
你應該可以想到,三個(gè)線(xiàn)程都被創(chuàng )建并start()
,這時(shí)候三個(gè)線(xiàn)程隨時(shí)都可能執行run()
方法。因此為了保證run()
執行的順序性,我們肯定需要一個(gè)信號量來(lái)讓線(xiàn)程知道在任意時(shí)刻能不能執行邏輯代碼。
另外,因為三個(gè)線(xiàn)程是獨立的,這個(gè)信號量的變化肯定需要對其他線(xiàn)程透明,因此volatile關(guān)鍵字也是必須要的。
public class TicketExample2 { //信號量 static volatile int ticket = 1; //線(xiàn)程休眠時(shí)間 public final static int SLEEP_TIME = 1; public static void foo(int name){ //因為線(xiàn)程的執行順序是不可預期的,因此需要每個(gè)線(xiàn)程自旋 while (true) { if (ticket == name) { try { Thread.sleep(SLEEP_TIME); //每個(gè)線(xiàn)程循環(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(); } }
執行結果:
1 0
1 1
1 2
2 0
2 1
2 2
3 0
3 1
3 2
此種方法的思想跟第三種方法是一樣的,都是不考慮線(xiàn)程執行的順序而是考慮用一些方法控制線(xiàn)程執行業(yè)務(wù)邏輯的順序。這里我們同樣用一個(gè)原子類(lèi)型信號量ticket
,當然你可以不用原子類(lèi)型,這里我只是為了保證自增操作的線(xiàn)程安全。然后我們用了一個(gè)可重入鎖ReentrantLock
。用來(lái)給方法加鎖,當一個(gè)線(xiàn)程拿到鎖并且標識位正確的時(shí)候開(kāi)始執行業(yè)務(wù)邏輯,執行完畢后喚醒下一個(gè)線(xiàn)程。
這里我們不需要使用while進(jìn)行自旋操作了,因為L(cháng)ock可以讓我們喚醒指定的線(xiàn)程,所以改成if就可以實(shí)現順序的執行。
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(); //因為線(xiàn)程的執行順序是不可預期的,因此需要每個(gè)線(xiàn)程自旋 System.out.println("線(xiàn)程" + name + " 開(kāi)始執行"); if(ticket.get() != name) { try { System.out.println("當前標識位為" + ticket.get() + ",線(xiàn)程" + name + " 開(kāi)始等待"); //開(kāi)始等待被喚醒 conditions[name - 1].await(); System.out.println("線(xiàn)程" + name + " 被喚醒"); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(name); ticket.getAndIncrement(); if (ticket.get() > 3) { ticket.set(1); } //執行完畢,喚醒下一次。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(); } }
輸出結果:
線(xiàn)程2 開(kāi)始執行
當前標識位為1,線(xiàn)程2 開(kāi)始等待
線(xiàn)程1 開(kāi)始執行
1
線(xiàn)程3 開(kāi)始執行
當前標識位為2,線(xiàn)程3 開(kāi)始等待
線(xiàn)程2 被喚醒
2
線(xiàn)程3 被喚醒
3
上述的執行結果并非唯一,但可以保證打印的順序一定是123這樣的順序。
以上就是Java中保證線(xiàn)程順序執行的詳細內容,更多關(guān)于java線(xiàn)程執行順序的資料請關(guān)注腳本之家其它相關(guān)文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站