以下程序为了学习而写
两个线程依次按序打印从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();
}
}
问题在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]
while(turn!=counter%num){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
turn不等于的时候,然后就wait,
线程调用了wait,就会进入休眠状态并且释放锁,直到其他线程调用相同对象的notify或者notifyAll,这个线程才会(notify的话只是有可能会)重新进入执行队列。当这个线程开始执行的时候它会再次接管锁并执行wait后面的内容(接管锁这个动作会等待锁被其他线程释放)。
楼主,根本原因是你这里用了两个不同的对象,是不会实现互斥效果的
[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]
首先回答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
至于如何修改,前面的答案都回答了