今天晚上在研究多线程相关问题的时候,写到如下程序
实现两个线程交替输出1 2 3 4 5 ······
public class ThreadTest {
public static void main(String[] args) {
Integer integer = new Integer(1); // 共享对象integer
// 共享对象传入两个线程中
Thread t1 = new Thread(new MyThread1(integer));
Thread t2 = new Thread(new MyThread2(integer));
// 运行
t1.start();
t2.start();
}
}
class MyThread1 implements Runnable {
private Integer integer;
// 通过构造方法传入共享对象
MyThread1(Integer integer) {
this.integer = integer;
}
@Override
public void run() {
while (true) {
synchronized (integer) {
if (integer % 2 == 0) { // 自动拆箱
try {
integer.wait(); // 偶数时线程1等待(同时释放锁)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "->" + integer);
integer++;
integer.notifyAll(); // 唤醒integer上的两个线程
}
}
}
}
class MyThread2 implements Runnable {
private Integer integer;
// 通过构造方法传入共享对象
MyThread2(Integer integer) {
this.integer = integer;
}
@Override
public void run() {
while (true) {
synchronized (integer) {
if (integer % 2 == 1) { // 自动拆箱
try {
integer.wait(); // 偶数时线程1等待(同时释放锁)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "->" + integer);
integer++;
integer.notifyAll(); // 唤醒integer上的两个线程
}
}
}
}
运行后报错信息如下:
Thread-0->1
Exception in thread “Thread-0” java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at MyThread1.run(MyTest.java:43)
at java.lang.Thread.run(Thread.java:748)
可见在Thread-0输出了第一条信息后发生Exception,notifyAll方法存在问题
在查阅相关资料后了解到
出现java.lang.IllegalMonitorStateException错误,由以下情况导致:
-
当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法;
-
当前线程不含有当前对象的锁资源的时候,调用obj.notify()方法。
-
当前线程不含有当前对象的锁资源的时候,调用obj.notifyAll()方法。
因此可能出现了情况3的问题
而调用此方法的integer上的的确确是有两个线程的
因此,可能是由于integer++操作不当
故查阅Integer源码:
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
/**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}
Integer中的value字段被final修饰不可更改,所以integer++操作底层实际上会new一个新对象,value为integer的value值加1,再将integer引用指向这个新的对象,而新的对象是没有上锁的,故报错。
public class MyTest {
public static void main(String[] args) {
Integer integer = 0;
System.out.println(System.identityHashCode(integer)); // 356573597
integer++;
//System.out.println(integer); // 1
System.out.println(System.identityHashCode(integer)); // 1735600054
}
}
最后再由上述代码进一步证明,integer++前后内存地址是不同的。