在 JAVA 中交替显示线程
Displaying threads alternatively in JAVA
我想像这样交替显示这两个线程:
- 线程 1
- 线程 0
- 线程 1
- 线程 0
...
这是我开始的基本代码,我尝试了 wait() notify() 方法,但我无法得到想要的结果。
class Task extends Thread {
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
double dure = Math.random()*200 ;
sleep((long) dure);
System.out.println(Thread.currentThread().getName());
}
} catch (Exception e) {
}
}
}
public class App {
public static void main(String[] args) {
Task t1 = new Task() ;
Task t2 = new Task() ;
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
}
}
} ```
随机休眠时间也会在 main 方法中导致意外结果,使主线程在 Thread1 和 Thread2 启动之间休眠可以帮助您了解谁是第一个启动打印任务的线程,之后你应该在任务中给予正确的休眠时间,让线程有可能交替打印。
class Task extends Thread {
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
sleep(2000);
System.out.println(Thread.currentThread().getName());
}
} catch (Exception e) {
}
}
}
public class App {
public static void main(String[] args) {
Task t1 = new Task() ;
Task t2 = new Task() ;
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
我看到两个解决方案:
- 忙等
每个线程在打印前等待。并在条件为真时释放。我将 AtomicInteger 用于 indexToPrint
以使每个线程的此值同步。
此解决方案适用于 n 个线程。
import java.util.concurrent.atomic.AtomicInteger;
class Task extends Thread {
final static private AtomicInteger indexToPrint = new AtomicInteger(0);
static private int threadNumber = 0;
final private int index;
/**
*
*/
public Task() {
index = threadNumber++;
}
private int nextIndex() {
return (index + 1) % threadNumber;
}
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
double dure = Math.random()*200 ;
sleep((long) dure);
while (indexToPrint.get() != index) {
sleep((long) 10);
}
indexToPrint.set(nextIndex());
System.out.println(Thread.currentThread().getName());
}
} catch (Exception e) {}
}
}
- 等待并通知
理解起来有点复杂,但没有无用的 CPU 使用。让我们解释一下同步块 synchronized (indexToPrint) {...}
是如何工作的。
该块同步监视静态对象 indexToPrint
。这个对象是静态的(所有线程通用),所以只有一个线程可以同时进入这个块。
当一个线程进入块时,如果它的 index
与 indexToPrint
不同,则该线程将停止并 wait()
使另一个线程可以进入块。否则,打印线程名称,indexToPrint
更新为下一个线程索引,所有线程都被 notifyAll()
唤醒。最后,它离开了街区。
所有等待的线程现在都醒了,真正的线程离开了块。所以一个线程可以再次尝试打印。
重要的是要理解,当一个线程被置于等待状态然后发出通知时,它会准确地运行在它停止的地方。在这里,线程可以在两个位置停止:同步块之前和等待调用。
while
在这里非常重要。所有线程都在 notifyAll()
醒来,所以在醒来后他们应该再次测试自己。
你可以找到一个好的documentation here。
代码基于上一个。同样使用 indexToPrint
.
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
class Task extends Thread {
static private final AtomicInteger indexToPrint = new AtomicInteger(0);
static private int threadNumber = 0;
final private int index;
final private static ArrayList<Task> tasks = new ArrayList<>();
/**
*
*/
public Task() {
index = threadNumber++;
tasks.add(this);
}
private int nextIndex() {
return (index + 1) % threadNumber;
}
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
double dure = Math.random()*200 ;
sleep((long) dure);
synchronized (indexToPrint) {
while (indexToPrint.get() != index) {
indexToPrint.wait();
}
indexToPrint.set(nextIndex());
System.out.println(Thread.currentThread().getName());
indexToPrint.notifyAll();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
我想像这样交替显示这两个线程:
- 线程 1
- 线程 0
- 线程 1
- 线程 0 ...
这是我开始的基本代码,我尝试了 wait() notify() 方法,但我无法得到想要的结果。
class Task extends Thread {
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
double dure = Math.random()*200 ;
sleep((long) dure);
System.out.println(Thread.currentThread().getName());
}
} catch (Exception e) {
}
}
}
public class App {
public static void main(String[] args) {
Task t1 = new Task() ;
Task t2 = new Task() ;
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
}
}
} ```
随机休眠时间也会在 main 方法中导致意外结果,使主线程在 Thread1 和 Thread2 启动之间休眠可以帮助您了解谁是第一个启动打印任务的线程,之后你应该在任务中给予正确的休眠时间,让线程有可能交替打印。
class Task extends Thread {
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
sleep(2000);
System.out.println(Thread.currentThread().getName());
}
} catch (Exception e) {
}
}
}
public class App {
public static void main(String[] args) {
Task t1 = new Task() ;
Task t2 = new Task() ;
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
我看到两个解决方案:
- 忙等
每个线程在打印前等待。并在条件为真时释放。我将 AtomicInteger 用于 indexToPrint
以使每个线程的此值同步。
此解决方案适用于 n 个线程。
import java.util.concurrent.atomic.AtomicInteger;
class Task extends Thread {
final static private AtomicInteger indexToPrint = new AtomicInteger(0);
static private int threadNumber = 0;
final private int index;
/**
*
*/
public Task() {
index = threadNumber++;
}
private int nextIndex() {
return (index + 1) % threadNumber;
}
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
double dure = Math.random()*200 ;
sleep((long) dure);
while (indexToPrint.get() != index) {
sleep((long) 10);
}
indexToPrint.set(nextIndex());
System.out.println(Thread.currentThread().getName());
}
} catch (Exception e) {}
}
}
- 等待并通知
理解起来有点复杂,但没有无用的 CPU 使用。让我们解释一下同步块 synchronized (indexToPrint) {...}
是如何工作的。
该块同步监视静态对象 indexToPrint
。这个对象是静态的(所有线程通用),所以只有一个线程可以同时进入这个块。
当一个线程进入块时,如果它的 index
与 indexToPrint
不同,则该线程将停止并 wait()
使另一个线程可以进入块。否则,打印线程名称,indexToPrint
更新为下一个线程索引,所有线程都被 notifyAll()
唤醒。最后,它离开了街区。
所有等待的线程现在都醒了,真正的线程离开了块。所以一个线程可以再次尝试打印。
重要的是要理解,当一个线程被置于等待状态然后发出通知时,它会准确地运行在它停止的地方。在这里,线程可以在两个位置停止:同步块之前和等待调用。
while
在这里非常重要。所有线程都在 notifyAll()
醒来,所以在醒来后他们应该再次测试自己。
你可以找到一个好的documentation here。
代码基于上一个。同样使用 indexToPrint
.
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
class Task extends Thread {
static private final AtomicInteger indexToPrint = new AtomicInteger(0);
static private int threadNumber = 0;
final private int index;
final private static ArrayList<Task> tasks = new ArrayList<>();
/**
*
*/
public Task() {
index = threadNumber++;
tasks.add(this);
}
private int nextIndex() {
return (index + 1) % threadNumber;
}
@Override
public void run() {
try {
for(int i = 0; i<10; i++){
double dure = Math.random()*200 ;
sleep((long) dure);
synchronized (indexToPrint) {
while (indexToPrint.get() != index) {
indexToPrint.wait();
}
indexToPrint.set(nextIndex());
System.out.println(Thread.currentThread().getName());
indexToPrint.notifyAll();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}