您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Java如何實現多線程循環打印,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
循環打印問題可以通過設置目標值,每個線程想打印目標值,如果拿到鎖后這次輪到的數不是它想要的就進入wait
class Wait_Notify_ABC { private int num; private static final Object Lock = new Object(); private void print_ABC(int target) { synchronized (Lock) { //循環打印 for (int i = 0; i < 10; i++) { while (num % 3 != target) { try { Lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } num++; System.out.print(Thread.currentThread().getName()); Lock.notifyAll(); } } } public static void main(String[] args) { Wait_Notify_ABC wait_notify_abc = new Wait_Notify_ABC(); new Thread(() -> { wait_notify_abc.print_ABC(0); }, "A").start(); new Thread(() -> { wait_notify_abc.print_ABC(1); }, "B").start(); new Thread(() -> { wait_notify_abc.print_ABC(2); }, "C").start(); } }
打印1-100問題可以理解為有個全局計數器記錄當前打印到了哪個數,其它就和循環打印ABC問題相同。
class Wait_Notify_100 { private int num; private static final Object LOCK = new Object(); private int maxnum = 100; private void printABC(int targetNum) { while (true) { synchronized (LOCK) { while (num % 3 != targetNum) { if (num >= maxnum) { break; } try { LOCK.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (num >= maxnum) { break; } num++; System.out.println(Thread.currentThread().getName() + ": " + num); LOCK.notifyAll(); } } } public static void main(String[] args) { Wait_Notify_100 wait_notify_100 = new Wait_Notify_100(); new Thread(() -> { wait_notify_100.printABC(0); }, "thread1").start(); new Thread(() -> { wait_notify_100.printABC(1); }, "thread2").start(); new Thread(() -> { wait_notify_100.printABC(2); }, "thread3").start(); } }
一個線程內調用另一個線程的join()方法可以讓另一個線程插隊執行,比如Main方法里調用了A.join(),那么此時cpu會去執行A線程中的任務,執行完后再看Main是否能搶到運行權。所以對于ABC,我們可以對B說讓A插隊,對C說讓B插隊
class Join_ABC { static class printABC implements Runnable { private Thread beforeThread; public printABC(Thread beforeThread) { this.beforeThread = beforeThread; } @Override public void run() { if (beforeThread != null) { try { beforeThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print(Thread.currentThread().getName()); } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++) { Thread t1 = new Thread(new printABC(null), "A"); Thread t2 = new Thread(new printABC(t1), "B"); Thread t3 = new Thread(new printABC(t2), "C"); t1.start(); t2.start(); t3.start(); Thread.sleep(100); } } }
同理,synchronized和reentrantlock都是我們常用的加鎖方式,不過后者可以中斷,可以實現公平鎖,可以使用condition…但是需要我們手動釋放鎖。jdk8后二者性能差不多,畢竟synchronized有鎖升級的過程嘛。
class ReentrantLock_ABC { private int num; private Lock lock = new ReentrantLock(); private void printABC(int targetNum) { for (int i = 0; i < 100; ) { lock.lock(); if (num % 3 == targetNum) { num++; i++; System.out.print(Thread.currentThread().getName()); } lock.unlock(); } } public static void main(String[] args) { Lock_ABC lockABC = new Lock_ABC(); new Thread(() -> { lockABC.printABC(0); }, "A").start(); new Thread(() -> { lockABC.printABC(1); }, "B").start(); new Thread(() -> { lockABC.printABC(2); }, "C").start(); } }
以上方式如果線程搶到鎖后發現自己無法執行任務,那么就釋放,然后別的線程再搶占再看是不是自己的…這種方式比較耗時,如果我們能實現精準喚醒鎖呢,即A完成任務后喚醒它的下一個即B,這就用到我們的Condition啦
class ReentrantLock_Condition_ABC { private int num; private static Lock lock = new ReentrantLock(); private static Condition c1 = lock.newCondition(); private static Condition c2 = lock.newCondition(); private static Condition c3 = lock.newCondition(); private void printABC(int targetNum, Condition currentThread, Condition nextThread) { for (int i = 0; i < 100; ) { lock.lock(); try { while (num % 3 != targetNum) { currentThread.await(); //阻塞當前線程 } num++; i++; System.out.print(Thread.currentThread().getName()); nextThread.signal(); //喚醒下一個線程 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } public static void main(String[] args) { ReentrantLock_Condition_ABC reentrantLockConditionAbc = new ReentrantLock_Condition_ABC(); new Thread(() -> { reentrantLockConditionAbc.printABC(0, c1, c2); }, "A").start(); new Thread(() -> { reentrantLockConditionAbc.printABC(1, c2, c3); }, "B").start(); new Thread(() -> { reentrantLockConditionAbc.printABC(2, c3, c1); }, "C").start(); } }
小伙伴們有沒有想到過,在生產者消費者模型中我們有哪幾種實現方式呢?wait\notify,ReentrantLock,Semaphone,阻塞隊列,管道輸入輸出流。
對的就是Semaphone。
Semaphore有acquire方法和release方法。 當調用acquire方法時線程就會被阻塞,直到獲得許可證為止。 當調用release方法時將向Semaphore中添加一個許可證。如果沒有獲取許可證的線程, Semaphore只是記錄許可證的可用數量。
使用Semaphore也可以實現精準喚醒。
class SemaphoreABC { private static Semaphore s1 = new Semaphore(1); //因為先執行線程A,所以這里設s1的計數器為1 private static Semaphore s2 = new Semaphore(0); private static Semaphore s3 = new Semaphore(0); private void printABC(Semaphore currentThread, Semaphore nextThread) { for (int i = 0; i < 10; i++) { try { currentThread.acquire(); //阻塞當前線程,即信號量的計數器減1為0 System.out.print(Thread.currentThread().getName()); nextThread.release(); //喚醒下一個線程,即信號量的計數器加1 } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { SemaphoreABC printer = new SemaphoreABC(); new Thread(() -> { printer.printABC(s1, s2); }, "A").start(); Thread.sleep(100); new Thread(() -> { printer.printABC(s2, s3); }, "B").start(); Thread.sleep(100); new Thread(() -> { printer.printABC(s3, s1); }, "C").start(); } }
關于“Java如何實現多線程循環打印”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。