保證順序性其實就是java多線程通信的問題。三個線程協定執行的先後順序,轉換成線程通信的問題,我需要用一個東西來協定執行到哪個線程了,這東西實作方式就有很多了:
Java的線程通信都是使用記憶體共享變量的方式,那麼就簡單的方式就是:
- 一個共享變量。這個共享變量可以是一個簡單的靜态變量,可以是一個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,應該是上下文互動太厲害了,如果讀者有更好的方式,歡迎告訴我,給我留言。大家互相學習,非常感謝。