当然我在扯淡

诡异的Integer

一个常见的面试题

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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();
}
}

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

1
2
3
4
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的源码

1
private final int value;

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

1
2
3
4
5
6
7
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了解一下,贴代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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();
}
}