Java 从三个不同的线程按顺序打印一、二、三?
我正在研究以下面试问题: 一个进程中有三个线程。第一条线打印1 …第二个打印2 2…第三个打印3 3 ... 没完没了。您如何安排这三个线程以 打印123123 我想出了下面的代码,它使用两个线程打印Java 从三个不同的线程按顺序打印一、二、三?,java,multithreading,concurrency,thread-safety,Java,Multithreading,Concurrency,Thread Safety,我正在研究以下面试问题: 一个进程中有三个线程。第一条线打印1 …第二个打印2 2…第三个打印3 3 ... 没完没了。您如何安排这三个线程以 打印123123 我想出了下面的代码,它使用两个线程打印1 2 1 2,但我无法从第三个线程中找出如何打印数字3的条件 public class PrintOneTwoThree { private static boolean isFirst = true; private static final Object lock = new Obje
1 2 1 2
,但我无法从第三个线程中找出如何打印数字3
的条件
public class PrintOneTwoThree {
private static boolean isFirst = true;
private static final Object lock = new Object();
public static void main(String[] args) {
// first thread
new Thread(() -> {
try {
synchronized (lock) {
for (;;) {
while (!isFirst) {
lock.wait();
}
System.out.print("1 ");
isFirst = false;
lock.notify();
}
}
} catch (InterruptedException ignored) {
}
}).start();
// second thread
new Thread(() -> {
try {
synchronized (lock) {
for (;;) {
while (isFirst) {
lock.wait();
}
System.out.print("2 ");
isFirst = true;
lock.notify();
}
}
} catch (InterruptedException ignored) {
}
}).start();
}
}
如何有效地解决这类问题?使用整数计数器,并在除以3时检查余数,每次打印后递增计数器,而不是使用布尔标志。 由于将有多个线程等待同一个锁,因此最好使用
notifyAll
private static int counter = 0;
new Thread(() -> {
try {
synchronized (lock) {
for (;;) {
while (counter % 3 != 0) {
lock.wait();
}
System.out.print("1 ");
++counter;
lock.notifyAll();
}
}
} catch (InterruptedException ignored) {
}
}).start();
//And the same stuff for other two threads just replacing value for remainder in if and value in print
下面是一个通用的工作示例,使用计数器一次为
n
线程授予一个线程权限:
public class PrintOneTwoThree {
private static int currentTask;
private static int totalThreads;
private static final Object lock = new Object();
public static void main(String[] args) {
currentTask = 0;
totalThreads = 3;
for (int i = 0; i < totalThreads; i++) {
createThread(i);
}
}
static void createThread(int id) {
new Thread(() -> {
try {
for (;;) {
synchronized (lock) {
while (currentTask != id) {
lock.wait();
}
System.out.print(id + 1 + " ");
currentTask = (currentTask + 1) % totalThreads;
lock.notifyAll();
}
}
}
catch (InterruptedException ignored) {}
}).start();
}
}
几句话:
适用于双线程版本(因为变量上最多有一个其他线程阻塞),但是如果一个线程退出临界区并通知s条件变量currentTask可用,但错误的线程赢得了获取锁的竞赛,则在3+版本中会死锁。我不确定这里的notify()
设计是否合适,因为只有一个线程可以取得进展,所以它似乎是重新检查条件谓词并使用notifyAll()
的替身notify()
- 我将(;;)的
移到了synchronized部分之外,以尽可能缩小线程安全范围。这个例子是人为设计的,因为如果您想要访问单个资源的确切行为,而不需要其他任何东西,那么增加线程的开销是没有意义的——您可以在单个线程中确定地执行它。在一个现实世界的例子中,当线程不阻塞条件变量时,线程将在
循环的其他地方执行线程安全工作,因此考虑到这一点进行设计似乎是合乎逻辑的for(;;)
public class PrintOneTwoThree {
public static void main(String[] args) {
Printers sp = new Printers();
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(new FirstNumberProducer(sp, 9));
executor.submit(new SecondNumberProducer(sp , 9));
executor.submit(new ThirdNumberProducer(sp , 9));
executor.shutdown();
}
}
class Printers {
Semaphore first = new Semaphore(1);
Semaphore second = new Semaphore(0);
Semaphore third = new Semaphore(0);
public void printFirstNumber() {
try {
first.acquire();
}catch(InterruptedException exception) {
}
System.out.print("1");
second.release();
}
public void printSecondNumber() {
try {
second.acquire();
}catch(InterruptedException exception) {
}
System.out.print("2");
third.release();
}
public void printThirdNumber() {
try {
third.acquire();
}catch(InterruptedException exception) {
}
System.out.print("3");
first.release();
}
}
class FirstNumberProducer implements Runnable {
Printers sp;
int index;
FirstNumberProducer(Printers sp , int index) {
this.sp = sp;
this.index = index;
}
@Override
public void run() {
for(int i = 1 ; i <= index ; i = i + 3 ) {
sp.printFirstNumber();
}
}
}
class SecondNumberProducer implements Runnable{
Printers sp;
int index;
SecondNumberProducer(Printers sp , int index) {
this.sp = sp;
this.index = index;
}
@Override
public void run() {
for(int i = 2 ; i <= index ; i = i + 3) {
sp.printSecondNumber();
}
}
}
class ThirdNumberProducer implements Runnable{
Printers sp;
int index;
ThirdNumberProducer(Printers sp , int index) {
this.sp = sp;
this.index = index;
}
@Override
public void run() {
for(int i = 3 ; i <= index ; i = i + 3) {
sp.printThirdNumber();
}
}
}
123123123