一个常见的面试题

Java语言实现两个线程交替输出1,2。 看到这个题就想起了生产者-消费者,进而想到了Synchorinzed, wait, notify。下面是我要写的代码

class PrintThread implements Runnable{
	private Integer num;
	private int mod;
	public PrintThread(Integer num, int mod){
		this.num = num;
		this.mod = mod;
	}

	public void run(){
		while(true){
			synchronized(num){
				while(num % 2 == mod) {
					try{
						num.wait();
					}catch(InterruptedException e){
						System.out.println(e);
					}
				}
				if(num > 100) break;
				System.out.println(Thread.currentThread().getName() + ":" + num);
				num++;
				num.notify();
			}
		}
	}
}

public class Main{
	public static void main(String[] args) {
		Integer num = 1;
		new Thread(new PrintThread(num, 1)).start();
		new Thread(new PrintThread(num, 2)).start();
	}
}

然而运行结果确实很经典的错误

Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	at PrintThread.run(Main.java:22)
	at java.lang.Thread.run(Thread.java:748)

很明显的Monitor未锁住。然而我明明传入的是同一个Integer,然后去看Integer的源码

private final int value;

Integer的value是final,无法改变的,那么 num++ 发生了什么呢

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
Interger a = Integer.valueOf(i);

所以 a++ 之后 返回的是一个新的对象,丢失了Monitor。 将Integer改为一个自定义的包装类后,一切正常。

No Synchronized

同样是这个问题,不用synchronized怎么写呢。 Condition了解一下,贴代码了。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class Num{
	int value;
}

class PrintThread implements Runnable{
	private Num num;
	private int mod;

	private ReentrantLock reentrantLock;
	private Condition condition;

	public PrintThread(Num num, int mod, 
			ReentrantLock reentrantLock, Condition condition){
		this.num = num;
		this.mod = mod;
		this.reentrantLock = reentrantLock;
		this.condition = condition;
	}

	public void run(){
		while(true){
			reentrantLock.lock();
			while(num.value % 2 == mod){
				try{
					condition.await();
				}catch(InterruptedException e){
					
				}

			}
			System.out.println(Thread.currentThread().getName() + ":" + num.value);
			num.value++;
			if(num.value > 100) break;
			condition.signal();
			reentrantLock.unlock();
		}
	}
}

public class Main{
	public static void main(String[] args) {
		Num num = new Num();
		num.value = 1;
		ReentrantLock reentrantLock = new ReentrantLock();
		Condition condition = reentrantLock.newCondition();

		new Thread(new PrintThread(num, 0, reentrantLock, condition)).start();
		new Thread(new PrintThread(num, 1, reentrantLock, condition)).start();
	}
}