[英]Java multithreading wait and notify in same block
我正在研究一个小问题,我需要以交替的方式顺序打印两个线程的数字。 像线程1打印1,线程2打印2,线程1打印3,依此类推......
所以我创建了下面的代码,但在某些时候,两个线程都进入等待状态,并且没有任何东西在控制台上打印。
import java.util.concurrent.atomic.AtomicInteger;
public class MultiPrintSequence {
public static void main(String[] args) {
AtomicInteger integer=new AtomicInteger(0);
Sequence sequence1=new Sequence(integer);
Sequence sequence2=new Sequence(integer);
sequence1.start();
sequence2.start();
}
}
class Sequence extends Thread{
private AtomicInteger integer;
boolean flag=false;
public Sequence(AtomicInteger integer) {
this.integer=integer;
}
@Override
public void run() {
while(true) {
synchronized (integer) {
while (flag) {
flag=false;
try {
System.out.println(Thread.currentThread().getName()+" waiting");
integer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" "+integer.incrementAndGet());
flag = true;
System.out.println(Thread.currentThread().getName()+" notifying");
integer.notify();
}
}
}
}
在观察控制台输出时,我注意到在其中一个线程通知的某个时刻,另一个线程最终在通知线程进入等待状态之前启动,因此在一个点上两个线程都进入等待状态。 下面是控制台输出的一小部分。
Thread-1 510
Thread-1 notifying
Thread-1 waiting
Thread-0 511
Thread-0 notifying
Thread-0 waiting
Thread-1 512
Thread-1 notifying
Thread-1 waiting
**Thread-0 513
Thread-0 notifying
Thread-1 514
Thread-1 notifying
Thread-1 waiting
Thread-0 waiting**
考虑一下这个不幸的事件序列。 Thread1递增该值,将该标志设置为true并通知等待集中的任何线程以进行锁定。 现在Thread0已经在等待集中了。 然后Thread0醒来,它的标志= false。 然后Thread0退出while循环并打印递增的值并通知任何等待的线程。 然后它继续进行while循环中的下一次迭代,并对锁定对象调用wait。 但是Thread1没有处于等待状态,而是在完成它的同步块之后由调度程序从CPU切换出来以给予Thread0机会。 Thread1处于可运行状态,由于没有剩余的可运行线程,因此调度程序有机会再次返回。 然后Tread1进入while循环,因为flag = true,并在同一个锁对象上调用wait。 现在两个线程都处于等待状态,并且没有人将它们唤醒。 所以这是系统中实时锁定的一个很好的例子。
发生这种情况是因为标志是一个实例字段,因此不在线程之间共享。 所以每个线程都有自己的标志副本。 如果将其标记为静态变量,则两个线程共享该值,因此问题得以解决。 这是标志声明的外观。
static boolean flag = false;
这是如何解决这个问题的? 好吧,考虑相同的事件序列。 现在,Thread1在锁定对象上调用notify之前将标志值设置为true。 Thread0已处于等待状态。 调度程序将Thread1从CPU切换下来,并为Thread0提供机会。 它开始运行,因为flag = true,它进入while循环将标志设置为false并调用锁定对象的wait。 然后Thread0进入等待状态,并且调度给了Thread1机会。 Thread1恢复执行并且flag = false,因此它退出while循环,打印递增的值并通知等待的Thread。 所以现在没有活锁。
但是,我没有看到使用synchronized
和非阻塞原子变量的任何意义。 你不应该同时使用它们。 下面给出了更好的,高性能的实现。
public class Sequence extends Thread {
private static final Object lock = new Object();
private static int integer = 0;
static boolean flag = false;
@Override
public void run() {
while (true) {
synchronized (lock) {
while (flag) {
flag = false;
try {
System.out.println(Thread.currentThread().getName() + " waiting");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " " + ++integer);
flag = true;
System.out.println(Thread.currentThread().getName() + " notifying");
lock.notify();
}
}
}
public static void main(String[] args) {
Sequence sequence1=new Sequence();
Sequence sequence2=new Sequence();
sequence1.start();
sequence2.start();
}
}
您的flag
变量不在线程之间共享,但该标志周围的逻辑仍然是奇数。 请注意,在使用synchronized
时不需要使用AtomicInteger
。
正确使用synchronized
时,普通的int
变量已足以实现整个逻辑:
public class MultiPrintSequence {
public static void main(String[] args) {
final Sequence sequence = new Sequence();
new Thread(sequence).start();
new Thread(sequence).start();
}
}
class Sequence implements Runnable {
private final Object lock = new Object();
private int sharedNumber;
@Override
public void run() {
synchronized(lock) {
for(;;) {
int myNum = ++sharedNumber;
lock.notify();
System.out.println(Thread.currentThread()+": "+myNum);
while(sharedNumber == myNum) try {
lock.wait();
} catch (InterruptedException ex) {
throw new AssertionError(ex);
}
}
}
}
}
当然,创建多个线程来执行顺序操作会破坏并发编程的实际目的。
在代码中,即使整数是Atomic并在Threads之间共享,标志本身也不是。
class Sequence extends Thread{
private AtomicInteger integer; //shared
boolean flag=false; //local
public Sequence(AtomicInteger integer) {
this.integer=integer;
}
这导致一个线程的变化没有反映在另一个线程中。
建议的解决方案:
您也可以使用Atomic解决标志并进行共享,例如:
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class StackOverflow {
public static void main(String[] args) {
AtomicInteger integer=new AtomicInteger(0);
AtomicBoolean flag=new AtomicBoolean(true);
Sequence sequence1=new Sequence(integer, flag);
Sequence sequence2=new Sequence(integer, flag);
sequence1.start();
sequence2.start();
}
}
顺序:
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
class Sequence extends Thread{
private final AtomicInteger integer;
private AtomicBoolean flag;
public Sequence(AtomicInteger integer, AtomicBoolean flag) {
this.integer=integer;
this.flag=flag;
}
@Override
public void run() {
while(true) {
synchronized (integer) {
while (flag.get()) {
flag.set(false);
try {
System.out.println(Thread.currentThread().getName()+" waiting");
integer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" "+integer.incrementAndGet());
flag.set(true);
System.out.println(Thread.currentThread().getName()+" notifying");
integer.notify();
}
}
}
}
这是输出的一部分:
Thread-1 8566
Thread-1 notifying
Thread-1 waiting
Thread-0 8567
Thread-0 notifying
Thread-0 waiting
Thread-1 8568
Thread-1 notifying
Thread-1 waiting
Thread-0 8569
Thread-0 notifying
Thread-0 waiting
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.