线程死锁问题

以下程序为了学习而写
两个线程依次按序打印从1到100,一个只打印奇数,另一个只打印偶数。但是经常发生死锁,请问为什么
public class Test16 implements Runnable{

private int turn;
private int num;
private int sum;
static Integer counter=0;
public Test16(int turn,int num,int sum){
    this.turn=turn;
    this.num=num;
    this.sum=sum;
}
public  synchronized void run() {
    while(counter<sum){
        while(turn!=counter%num){
            try {
                wait();
            } catch (InterruptedException e) {
                                    e.printStackTrace();
            }
        }

            counter++;
            System.out.print(counter+" "); 
            notifyAll();


    }

}

public static void main(String[] args){
    new Thread(new Test16(0,2,100)).start();
     new Thread(new Test16(1,2,100)).start();
}

}

5个回答

问题在public synchronized void run() { 相当于 synchronized(this),而 main中
new Thread(new Test16(0,2,100)).start();
new Thread(new Test16(1,2,100)).start();
这样是2个不同的test16对象,因而是2个不同的锁,2个锁都在等待自己的资源(2个线程都停止了),所以不可能被唤醒。其实楼主想锁定的是共享的counter,counter的是static的,属于类对象,所以要在类上加锁才行。
代码修改如下:
[code="java"]
public void run() {
synchronized (Test16.class) {
while (counter < sum) {
while (turn != counter % num) {
try {
Test16.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

            counter++;
            System.out.println(counter + "   turn : " + turn);
            Test16.class.notifyAll();

        }
    }

}

[/code]

fxhu09
fxhu09 可以运行结束。我也同意你的看法,两个线程确实等待的不是同一个锁。应该就是这个原因
5 年多之前 回复
guazixing
guazixing 你那里可以运行结束吗?我这里试的结果是每次都会卡到,只是有时候输出的数据多一点,这点我暂时也没有想到原因。在程序卡住时,用jstack查看线程,就可以看到两个线程确实等待的不是同一个锁 "Thread-1" prio=6 tid=0x000000000691f000 nid=0x1228 in Object.wait() [0x00000000 072bf000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007d6f13118> (a thread.Test16) at java.lang.Object.wait(Object.java:485) at thread.Test16.run(Test16.java:20) - locked <0x00000007d6f13118> (a thread.Test16) at java.lang.Thread.run(Thread.java:662) "Thread-0" prio=6 tid=0x000000000691e000 nid=0x1da8 in Object.wait() [0x00000000 071bf000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007d6f12fa0> (a thread.Test16) at java.lang.Object.wait(Object.java:485) at thread.Test16.run(Test16.java:20) - locked <0x00000007d6f12fa0> (a thread.Test16) at java.lang.Thread.run(Thread.java:662)
5 年多之前 回复
fxhu09
fxhu09 你的修改确实没有发生死锁。但是我的程序如果死锁没有发生时,却可以实现这个功能。(发生死锁的概率大概有60%),我感觉问题的根本原因不是你所说的。
5 年多之前 回复

while(turn!=counter%num){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

turn不等于的时候,然后就wait,
线程调用了wait,就会进入休眠状态并且释放锁,直到其他线程调用相同对象的notify或者notifyAll,这个线程才会(notify的话只是有可能会)重新进入执行队列。当这个线程开始执行的时候它会再次接管锁并执行wait后面的内容(接管锁这个动作会等待锁被其他线程释放)。

u010796208
黄菲 你的第二个while判断不对,你不就是想同时两个线程,然后执行第一个线程的时候第二个线程等待,执行第二个线程的时候第一个等待,也就是奇数的时候偶数等待,那你现在的判断是什么,你有判断wait()是哪个线程吗,你只是判断了他是奇数,等待,但是两个线程呢,你让他等待哪个,
5 年多之前 回复
fxhu09
fxhu09 和没说一样。为了实现这个功能,你能将它改好吗?
5 年多之前 回复

楼主,根本原因是你这里用了两个不同的对象,是不会实现互斥效果的
[quote]
new Thread(new Test16(0,2,100)).start();
new Thread(new Test16(1,2,100)).start();
[/quote]

这里两个Test16的对象,每个对象都可以进入你synchronized 的run方法。
这了互斥可以用类似生产者消费者李模式这种概念,或者直接使用阻塞队列,开始时队列只存放一个1,两个线程轮流每次在队列里取,取到消费后,将加1的结果再放入队列,此时notifyAll,另一个线程就可以取到数据。当前线程就会阻塞,以此可以实现你要的效果。

可以先看看notifyAll和wait的javadoc:“This method should only be called by a thread that is the owner of this object's monitor”,你这个程序的问题就是你把线程实例本身当做锁,但多个线程是不同的实例,就不是同一把锁了,所以程序比较混乱,可以通过简单添加一把锁可以解决:
[code="java"]
public class Test16 implements Runnable {

private int    turn;
private int    num;
private int    sum;
static Integer counter = 0;
static Object  lock    = new Object();

public Test16(int turn, int num, int sum){
    this.turn = turn;
    this.num = num;
    this.sum = sum;
}

public void run() {
    synchronized (lock) {
        while (counter < sum) {
            while (turn != counter % num) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            counter++;
            if (counter <= sum) {
                System.out.print(counter + " ");
            }
            lock.notifyAll();
        }
    }
}

public static void main(String[] args) {
    new Thread(new Test16(0, 3, 100), "A").start();
    new Thread(new Test16(1, 3, 100), "B").start();
    new Thread(new Test16(2, 3, 100), "C").start();
}

}
[/code]

fxhu09
fxhu09 你的也对。但是guazi发表在前。所以采纳了他的。谢谢你了。
5 年多之前 回复

首先回答lz的问题,为什么会死锁。代码稍微改一下:
[code="java"]while (counter < sum) {
while (turn != counter % num) {
try {
System.out.println(Thread.currentThread().getName() + " is waiting, " +
"count=" + counter + ", turn=" + turn);
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

        counter++;
        System.out.print(counter + " ");
        notifyAll();

    }[/code]

增加了一条打印语句。
一个可能的输出:
[code="java"]1 Thread-0 is waiting, count=1, turn=0
2 Thread-1 is waiting, count=2, turn=1
[/code]
看到没,虽然counter改变了,但由于线程1和线程2不是在同一把锁上进行同步,因此counter的改变并没有触发notifyAll

至于如何修改,前面的答案都回答了

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!