天天看點

有三個線程怎麼保證順序性

保證順序性其實就是java多線程通信的問題。三個線程協定執行的先後順序,轉換成線程通信的問題,我需要用一個東西來協定執行到哪個線程了,這東西實作方式就有很多了:

Java的線程通信都是使用記憶體共享變量的方式,那麼就簡單的方式就是:

  1. 一個共享變量。這個共享變量可以是一個簡單的靜态變量,可以是一個Semaphore等。

其實如果考慮搶鎖會消耗性能,如果我不跑的線程進行挂起呢?

實作方式2

2)線程的wait和notify

如果對底層了解一點,我個人了解性能最高的方式其實是直接使用LockSupport.park()

實作方式3:

LockSupport.park()

我們把這個題目具體化點:

三個線程,A線程列印A,B線程列印B,C線程列印C,按順序依次列印10_0000次,輸出ABCABCABCABCABCABCABCABCABCABC……。

下面是我實作的代碼,除了實作功能之外,我還想證明下自己的想法:實作方式3是最優的方案。

第一種方式:

public class PrintAbc {
    private int n;
    private static long statTime;

    private Semaphore aSemaphore = new Semaphore(1);
    private Semaphore bSemaphore = new Semaphore(0);
    private Semaphore cSemaphore = new Semaphore(0);

    static Thread threadA = null;
    static Thread threadB = null;
    static Thread threadC = null;

    public void printA() throws InterruptedException {

        for (int i = 0; i < n; i++) {
            aSemaphore.acquire();
            System.out.print("A");
            bSemaphore.release();
        }
    }

    public void printB() throws InterruptedException {

        for (int i = 0; i < n; i++) {
            bSemaphore.acquire();
            System.out.print("B");
            cSemaphore.release();
        }
        System.out.println("elapse time: "+(System.currentTimeMillis() - statTime));
    }
    public void printC() throws InterruptedException {

        for (int i = 0; i < n; i++) {
            cSemaphore.acquire();
            System.out.print("C");
            aSemaphore.release();
        }
    }

    public static void main(String[] args) {
        PrintAbc printAbc = new PrintAbc(10_0000);
        Runnable printA = () -> {
            try {
                printAbc.printA();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable printB = () -> {
            try {
                printAbc.printB();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable printC = () -> {
            try {
                printAbc.printC();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        threadA = new Thread(printA);
        threadB = new Thread(printB);
        threadC = new Thread(printC);
        statTime = System.currentTimeMillis();
        threadA.start();
        threadB.start();
        threadC.start();
    }

}
           

最終平均消耗的時間是:

elapse time: 2282
           

第二種方式感興趣的童鞋自己實作下,但是理論上性能不會高到哪去。

第三種實作:

public class PrintAbc {

    private int n;
    private static long statTime;

    public PrintAbc(int n) {
        this.n = n;
    }

    static Thread threadA = null;
    static Thread threadB = null;
    static Thread threadC = null;

    public void printA() throws InterruptedException {

        for (int i = 0; i < n; i++) {
            LockSupport.park(threadA);
            System.out.print("A");
            LockSupport.unpark(threadB);
        }
    }

    public void printB() throws InterruptedException {

        for (int i = 0; i < n; i++) {
            LockSupport.park(threadB);
            System.out.print("B");
            LockSupport.unpark(threadC);
        }
        System.out.println("elapse time: "+(System.currentTimeMillis() - statTime));
    }
    public void printC() throws InterruptedException {

        for (int i = 0; i < n; i++) {
        	LockSupport.park(threadC);
            System.out.print("C");
            LockSupport.unpark(threadA);
        }
    }

    public static void main(String[] args) {
        PrintAbc printAbc = new PrintAbc(10_0000);
        Runnable printA = () -> {
            try {
                printAbc.printA();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable printB = () -> {
            try {
                printAbc.printB();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable printC = () -> {
            try {
                printAbc.printC();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        threadA = new Thread(printA);
        threadB = new Thread(printB);
        threadC = new Thread(printC);
        statTime = System.currentTimeMillis();
        threadA.start();
        threadB.start();
        threadC.start();

        LockSupport.unpark(threadA);
    }
}

           

最終平均消耗時間為:

而且測試的過程中,有的時候LockSupport的方式耗時比aSemaphore,應該是上下文互動太厲害了,如果讀者有更好的方式,歡迎告訴我,給我留言。大家互相學習,非常感謝。