notifywait实现的消费和生产,并且注意Integer作为共享变量需要注意,拆箱装箱后会出现新的对象

/ 默认分类 / 0 条评论 / 899浏览
package com.example.demo.mytest;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class MainTest {

    static List<Integer> i = new ArrayList<>();

    //减少数量
    static Thread thread01 = new Thread(() -> {
        System.out.println("这是减少程序线程的运行");
        while (true) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (i) {
                while (i.size() < 5) {
                    System.out.println("已经减少到了极限,减少线程开始睡觉");
                    try {
                        i.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("减1,并且唤醒增加线程");
                //如果数量还足够那就继续减少,并且需要设置唤醒其他的线程
                i.remove(i.size() - 1);
                System.out.println(i.size());
                i.notifyAll();
            }
        }
    },"减少线程");

    //增加数量
    static Thread thread02 = new Thread(() -> {
        System.out.println("这是增加线程的启动");
        while (true) {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (i) {
                while (i.size() == 10) {
                    System.out.println("已经增加到了极限,停止增加!增加线程开始睡觉");
                    try {
                        i.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("加1,并且唤醒减少的线程");
                i.add(new Random().nextInt());
                System.out.println(i.size());
                i.notifyAll();
            }
        }
    },"增加线程");


    public static void main(String[] args) {
        for (int j = 0; j < 4; j++) {
            i.add(new Random().nextInt());
        }
        thread01.start();
        thread02.start();
    }
}

这是减少程序线程的运行
这是增加线程的启动
加1,并且唤醒减少的线程
5
减1,并且唤醒增加线程
4
加1,并且唤醒减少的线程
5
加1,并且唤醒减少的线程
6
加1,并且唤醒减少的线程
7
减1,并且唤醒增加线程
6
加1,并且唤醒减少的线程
7
减1,并且唤醒增加线程
6
加1,并且唤醒减少的线程
7
加1,并且唤醒减少的线程
8
加1,并且唤醒减少的线程
9
减1,并且唤醒增加线程
8
加1,并且唤醒减少的线程
9
加1,并且唤醒减少的线程
10
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
已经增加到了极限,停止增加!增加线程开始睡觉
减1,并且唤醒增加线程
9
加1,并且唤醒减少的线程
10
..................

出现这个问题的原因是java的自动装箱/拆箱机制,通过查看编译之后的.class文件可以清楚地看出,在执行 sMutex++语句时,其实执行的是sMutex= Integer.valueOf(sMutex.intValue() + 1)。在sMutex++执行前后分别调用System.identityHashCode(sMutex),计算所得结果已经不同了,说明sMutex已经指向了一个新的Integer对象,而锁的信息是存储在对象中的,而线程没有获取新对象的锁,所以报了IllegalMonitorStateException异常。

部分引用博客