使用synchronized和volatile来实现
通过在关键代码块或方法前添加synchronized关键字,可以确保同一时间只有一个线程能够执行该代码块或方法。
使用volatile修饰符,以确保修饰的字段在不同线程之间的可见性,它是被多个线程共享的。
工作原理
在上述示例中,printLetter()方法被声明为synchronized,这意味着同一时间只有一个线程能够执行这个方法。每个线程在执行printLetter()方法之前都会检查currentThread的值是否等于自己的threadId,如果不等于,则进入等待状态,直到其他线程执行完毕并释放锁。
当某个线程的threadId与currentThread相同时,线程可以执行打印操作,并将currentThread更新为下一个线程的threadId,然后唤醒所有等待的线程。
通过这种方式,每个线程按照指定的顺序获取锁并执行打印操作,从而实现了线程的顺序执行。
需要注意的是,我们在currentThread字段前面使用了volatile修饰符,以确保不同线程之间的可见性,这是因为该字段会被多个线程共享。
具体代码
public class ABCPrinter {
private volatile int currentThread = 0;
public synchronized void printLetter(String letter, int threadId){
while (threadId != currentThread) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(letter);
currentThread = (currentThread + 1) % 3;
notifyAll();
}
public static void main(String[] args) {
ABCPrinter printer = new ABCPrinter();
Thread threadA = new Thread(() -> {
for (int i = 0; i < 10; i++) {
printer.printLetter("a", 0);
}
});
Thread threadB = new Thread(() -> {
for (int i = 0; i < 10; i++) {
printer.printLetter("b", 1);
}
});
Thread threadC = new Thread(() -> {
for (int i = 0; i < 10; i++) {
printer.printLetter("c", 2);
}
});
threadA.start();
threadB.start();
threadC.start();
}
}
使用信号量来实现
当需要按照指定的顺序执行多线程任务时,可以使用一些同步机制来实现。在这种情况下,可以考虑使用Java中的信号量(Semaphore)来控制线程的执行顺序。
工作原理
在上述示例中,使用三个信号量semaphoreA,semaphoreB和semaphoreC来控制线程的执行顺序。
初始时,semaphoreA可用,而semaphoreB和semaphoreC不可用。printA()方法首先获取semaphoreA的许可,打印字符"a",然后释放semaphoreB的许可,使得printB()方法可以执行。
类似地,printB()方法获取semaphoreB的许可,打印字符"b",然后释放semaphoreC的许可,使得printC()方法可以执行。printC()方法获取semaphoreC的许可,打印字符"c",然后释放semaphoreA的许可,使得printA()方法可以再次执行,从而实现了循环的输出"abc"。
具体代码
import java.util.concurrent.Semaphore;
public class ABCPrinter {
private Semaphore semaphoreA;
private Semaphore semaphoreB;
private Semaphore semaphoreC;
public ABCPrinter(){
semaphoreA = new Semaphore(1);
semaphoreB = new Semaphore(0);
semaphoreC = new Semaphore(0);
}
public void printA(){
try{
semaphoreA.acquire();
System.out.println("a");
semaphoreB.release();
} catch(InterruptedException e){
e.printStackTrace();
}
}
public void printB(){
try{
semaphoreB.acquire();
System.out.println("b");
semaphoreC.release();
} catch(InterruptedException e){
e.printStackTrace();
}
}
public void printC(){
try{
semaphoreC.acquire();
System.out.println("c");
semaphoreA.release();
} catch(InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
ABCPrinter printer = new ABCPrinter();
Thread threadA = new Thread(() -> {
for (int i = 0; i < 10; i++) {
printer.printA();
}
});
Thread threadB = new Thread(() -> {
for (int i = 0; i < 10; i++) {
printer.printB();
}
});
Thread threadC = new Thread(() -> {
for (int i = 0; i < 10; i++) {
printer.printC();
}
});
threadA.start();
threadB.start();
threadC.start();
}
}