两种方式实现:
- Semaphore
- volatile变量
使用Semaphore
思路, 三个信号量, a控制b, b控制c, c控制a
初始情况, a开启, bc关闭
public class printByTurn {
private static Semaphore[] semaphores = new Semaphore[3];
static {
//初始情况
for(int i=0; i<semaphores.length; i++){
if(i==0) semaphores[i] = new Semaphore(1);
else semaphores[i] = new Semaphore(0);
}
}
static class thread extends Thread {
private int num;//当前线程对应信号量索引
private char printDigit;//当前线程打印字符
public thread(int num, char printDigit){
this.num = num;
this.printDigit = printDigit;
}
@Override
public void run() {
try{
for(int i=0; i<10; i++){
semaphores[this.num].acquire();
System.out.println(printDigit);
semaphores[(this.num+1)%3].release();
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new thread(0,'A').start();
new thread(1,'B').start();
new thread(2,'C').start();
}
}
使用volatile修饰的变量
public class printByTurn {
private static volatile boolean[] valatiles = new boolean[3];
static {
for(int i=0; i<semaphores.length; i++){
if(i==0) valatiles[i] = true;
else valatiles[i] = false;
}
}
static class thread extends Thread {
private int num;
private char printDigit;
public thread(int num, char printDigit){
this.num = num;
this.printDigit = printDigit;
}
@Override
public void run() {
try{
int i = 0;
while(true){
if(valatiles[num]){
valatiles[num] = false;
System.out.println(printDigit);
valatiles[(num+1)%3] = true;
i++;
}
if(i>=10) break;
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new thread(0,'A').start();
new thread(1,'B').start();
new thread(2,'C').start();
}
}
二者比较
- 前者使用juc提供的信号量, 通过aqs提供的机制, 实现线程的阻塞与线程的切换. 对于任务不是那么密集的场景, 信号量比较适合. 但如果任务密集, 则会疯狂的线程上下文切换, 内核态和用户态切换, 极其的影响性能.
- 后者使用volatile关键字, 通过volatile关键字来保证变量的可见性, 通过变量来控制当前是否打印, 然后每次打印过程就是先灭自己, 再点亮别人. 可以看到, 因为没有办法实现线程的阻塞, 这里实际是一个自旋锁, cpu会空转, 所以比较适合密集切换的任务. 对于不密集的任务, cpu空转十分消耗资源, 不如释放cpu的使用权, 采用前者方案.