多線程之按順序打印結果

前言

文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩qq了,但是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵大家在技術的路上寫博客

絮叨

這個是一個很簡單的多線程問題,但是小六六一看覺得很簡單,但是真正寫起來也不是那麼簡單,說明小六六自己的代碼還是寫的太少了哈哈

題目

我們提供了一個類:

public class Foo {
  public void one() { print("one"); }
  public void two() { print("two"); }
  public void three() { print("three"); }
}

三個不同的線程將會共用一個 Foo 實例。

  • 線程 A 將會調用 one() 方法
  • 線程 B 將會調用 two() 方法
  • 線程 C 將會調用 three() 方法

請設計修改程序,以確保 two() 方法在 one() 方法之後被執行,three() 方法在 two() 方法之後被執行。

示例 1:

輸入: [1,2,3]
輸出: "onetwothree"
解釋: 
有三個線程會被異步啓動。
輸入 [1,2,3] 表示線程 A 將會調用 one() 方法,線程 B 將會調用 two() 方法,線程 C 將會調用 three() 方法。
正確的輸出是 "onetwothree"。

題解一 synchronized 鎖和控制變量

package com.code.thread;

/**
 * 上面方法就是採用一個synchronized wait notifyAll  這些來實現的,但是吧,還需要一個while去自旋,還得靠一個信號量,感覺有點low。
 */

class Foo {

    private  int flag=0;
    private Object lock=new Object();
    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {
        synchronized(lock){
            while (flag!=0){
                lock.wait();
            }
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            flag=1;
            lock.notifyAll();
        }

    }

    public void second(Runnable printSecond) throws InterruptedException {
        synchronized (lock) {
            while (flag != 1) {
                lock.wait();
            }
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            flag = 2;
            lock.notifyAll();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {
        synchronized(lock) {
            while (flag != 2) {
                lock.wait();
            }
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
            lock.notifyAll();
        }
    }

    public static void main(String[] args) throws Exception {
        final Foo foo = new Foo();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.third(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(3);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t3.start();
        t2.start();
        t1.start();

    }
}

上面方法就是採用一個synchronized wait notifyAll 這些來實現的,但是吧,還需要一個while去自旋,還得靠一個信號量,感覺有點low。

題解二 用CountDownLatch來控制

  • countDownLatch這個類使一個線程等待其他線程各自執行完畢後再執行。
  • 是通過一個計數器來實現的,計數器的初始值是線程的數量。每當一個線程執行完畢後,計數器的值就-1,當計數- 器的值爲0時,表示所有線程都執行完畢,然後在閉鎖上等待的線程就可以恢復工作了。
package com.code.thread;

import java.util.concurrent.CountDownLatch;

public class Foo1 {
    //定義2個countDownLatch
    private CountDownLatch countDownLatchA=new CountDownLatch(1); //說明只要一個線程調用它就放行 ,它是到0就放行
    private CountDownLatch countDownLatchB=new CountDownLatch(1);
    public Foo1() {

    }

    public void first(Runnable printFirst) throws InterruptedException {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();


    }

    public void second(Runnable printSecond) throws InterruptedException {
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();

    }

    public void third(Runnable printThird) throws InterruptedException {
            // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();

    }

    public static void main(String[] args) throws Exception {
        final Foo1 foo = new Foo1();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                    foo.countDownLatchA.countDown();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.countDownLatchA.await();
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                    foo.countDownLatchB.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                    try {
                        foo.countDownLatchB.await();
                        foo.third(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(3);
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        });
        t3.start();
        t2.start();
        t1.start();

    }
}

方法三 信號量

基於信號量的解題思路

  • Semaphore 是什麼?
  • Semaphore 是一個計數信號量,必須由獲取它的線程釋放。
package com.code.thread;

import java.util.concurrent.Semaphore;

/**
 *
 * 基於信號量的解題思路
 * Semaphore 是什麼?
 * Semaphore 是一個計數信號量,必須由獲取它的線程釋放。
 *
 * 常用於限制可以訪問某些資源的線程數量,例如通過 Semaphore 限流。
 */
public class Foo2 {

    //初始化Semaphore爲0的原因:
    // 如果這個Semaphore爲零,如果另一線程調用(acquire)這個Semaphore就會產生阻塞,
    // 便可以控制second和third線程的執行
    private Semaphore spa=new Semaphore(0) ;
    private Semaphore spb=new Semaphore(0);




    public Foo2() {

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();
        spa.release();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        // printSecond.run() outputs "second". Do not change or remove this line.
        spa.acquire();
        printSecond.run();
        spb.release();
    }

    public void third(Runnable printThird) throws InterruptedException {
        // printThird.run() outputs "third". Do not change or remove this line.
        spb.acquire();
        printThird.run();
    }

    public static void main(String[] args) throws Exception {
        final Foo2 foo = new Foo2();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.first(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(1);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.second(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(2);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    foo.third(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(3);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t3.start();
        t2.start();
        t1.start();

    }
}

解題四 LockSupport

歸根結底,LockSupport調用的Unsafe中的native代碼:

  • public native void unpark(Thread jthread);
  • public native void park(boolean isAbsolute, long time);
  • 兩個函數聲明清楚地說明了操作對象:park函數是將當前Thread阻塞,而unpark函數則是將另一個Thread喚醒。
package com.code.thread;

import java.util.concurrent.locks.LockSupport;

/**'
 * 歸根結底,LockSupport調用的Unsafe中的native代碼:
 * public native void unpark(Thread jthread);
 * public native void park(boolean isAbsolute, long time);
 * 兩個函數聲明清楚地說明了操作對象:park函數是將當前Thread阻塞,而unpark函數則是將另一個Thread喚醒。
 *
 * 與Object類的wait/notify機制相比,park/unpark有兩個優點:1. 以thread爲操作對象更符合阻塞線程的直觀定義;2. 操作更精準,可以準確地喚醒某一個線程(notify隨機喚醒一個線程,notifyAll喚醒所有等待的線程),增加了靈活性。
 */
public class Foo3 {
    static Thread t1=null,t2=null,t3=null;

    public Foo3() {

    }

    public void first(Runnable printFirst) throws InterruptedException {

        printFirst.run();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
    }

    public void third(Runnable printThird) throws InterruptedException {
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }


        public static void main(String[] args) throws Exception {
            final Foo3 foo = new Foo3();

             t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        foo.first(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(1);
                            }
                        });
                        LockSupport.unpark(t2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
             t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.park();
                    try {
                        foo.second(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(2);
                            }
                        });
                        LockSupport.unpark(t3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
             t3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.park();
                    try {
                        foo.third(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(3);
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t3.start();
            t2.start();
            t1.start();

    }
}

結尾

其實解法有很多,如果多線程不熟悉的小夥伴,可以去我的github博客,先熟悉一下。這題應該算簡單的題型了。

日常求贊

好了各位,以上就是這篇文章的全部內容了,能看到這裏的人呀,都是真粉

創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見

六脈神劍 | 文 【原創】如果本篇博客有任何錯誤,請批評指教,不勝感激 !

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章