保证顺序性其实就是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);
}
}
最终平均消耗时间为:
elapse time: 1948
而且测试的过程中,有的时候LockSupport的方式耗时比aSemaphore,应该是上下文交互太厉害了,如果读者有更好的方式,欢迎告诉我,给我留言。大家互相学习,非常感谢。