[英]Make even and odd threads to print numbers in natural order in Java
我知道之前已經問過這個問題,但我無法弄清楚為什么我的解決方案對我不起作用。 我有兩個線程偶數和奇數,一個打印偶數和其他打印奇數。 當我啟動線程時,我希望輸出是自然的數字順序,如0 1 2 3 ..等。 這是我的代碼: - [更新]
public class ThreadCommunication {
public static void main(String... args) throws InterruptedException
{
final ThreadCommunication obj = new ThreadCommunication();
Thread even = new Thread(){
@Override
public void run()
{
for(int i=0;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
obj.notify();
}
}
}
};
even.start();
odd.start();
}
}
當我運行上面的代碼時,有時會按照預期自然順序打印數字,但有時它會以其他順序打印出來:
0
1
3
5
7
9
2
我在這做錯了什么?
編輯:
volatile static boolean isAlreadyWaiting = false;
Thread even = new Thread() {
@Override
public void run() {
synchronized (obj) {
for (int i = 0; i < 10; i = i + 2) {
System.out.println(i);
try {
if (!isAlreadyWaiting) {
isAlreadyWaiting = true;
obj.wait();
}
obj.notify();
isAlreadyWaiting=false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread() {
@Override
public void run() {
synchronized (obj) {
for (int i = 1; i < 10; i = i + 2) {
System.out.println(i);
try {
if(isAlreadyWaiting){
obj.notify();
isAlreadyWaiting = false;
}
if (!isAlreadyWaiting) {
isAlreadyWaiting = true;
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
檢查文檔
公共類IllegalMonitorStateException擴展RuntimeException
拋出此異常表示線程已嘗試在對象的監視器上等待 ,或者在沒有指定監視器的情況下通知在對象監視器上等待的其他線程。
Monitor由obj
擁有
所以你應該打電話
obj.wait();
和
obj.notify();
有關所有權的更多信息
此方法(等待或通知)只應由作為此對象監視器所有者的線程調用。 線程以三種方式之一成為對象監視器的所有者:
- 通過執行該對象的同步實例方法。
通過執行在對象上同步的synchronized語句的主體。
- 對於Class類型的對象,通過執行該類的同步靜態方法。
一次只有一個線程可以擁有對象的監視器。
@Pragnani Kinnera對你看到的例外是對的。 但是如果你想在even
和odd
之間交替,你需要將第二個同步塊移動到循環中。 否則,通知線程將保持鎖定,直到循環完成。 (與第一個線程相反,第一個線程在每一輪產生鎖定。)
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
notify();
}
}
}
};
但是,第一個線程應該在synchronized塊內部有循環。 如果兩個線程都釋放鎖定,則它們都有相同的機會重新獲取它。 但是如果第一個循環在同步塊內部,則第二個線程將無法重新進入,直到第一個循環完成整輪並再次等待。
編輯:這仍然無法正常工作,因為無法保證第一個線程在第二個線程執行之前不會重新獲取鎖定,根據文檔中的引用:
喚醒的線程將以通常的方式與可能主動競爭同步此對象的任何其他線程競爭; 例如,喚醒線程在下一個鎖定此對象的線程中沒有可靠的特權或劣勢。
你可能想要從兩個線程喚醒並通知它們以確保它們是同步的。
這是你的解決方案:
public class ThreadCommunication {
public static void main(String args[]) throws InterruptedException
{
final ThreadCommunication obj = new ThreadCommunication();
Thread even = new Thread("Even Thread"){
@Override
public void run()
{
for(int i=0;i<10;i=i+2){
System.out.println(i);
synchronized(obj){
obj.notify();
}
synchronized(obj){
try {
obj.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
try {
synchronized(obj){
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
synchronized(obj){
obj.notifyAll();
}
}
}
};
even.start();
odd.start();
}
}
正如@shmosel所解釋的,您的synchronized塊應該只包含需要同步的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.