一个常见的面试题
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();
}
}