[英]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.