关于Java中Interge对象自增出现IllegalMonitorStateException的理解(已解决)

今天在做一道作业题(附代码和输出结果):

使用生产者和消费者模式实现,交替输出:
   假设只有两个线程,输出以下结果:
      t1-->1
      t2-->2
      t1-->3
      t2-->4
      要求:必须交替,并且t1线程负责输出奇数。t2线程负责输出偶数。
      两个线程共享一个数字,每个线程执行时都要对这个数字进行:++

这道作业题看似简单,但却有一个坑,我们都知道synchronized拿到对象的锁,再使用完释放锁后,其他线程如果要获取这个对象锁,这个对象的内存地址一定不能够改变,不然就找不到了,最后大家都陷入到了无限的等待。

然而对于Java中的Interge对象有一个特殊的地方,就是在jvm的方法区中有整形常量池,而且有自动拆箱和装箱的操作,拆分操作:先将nums对象拆箱转化为基本数据类型的整形,然后执行nums++,最后再重新赋值给一个新的整形对象,所以当一个线程执行完这个操作:

synchronized (nums){
    if (nums%2==0){
        try {
            nums.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println(Thread.currentThread().getName()+"----->num:"+nums++);
    nums.notify(); 
}

这个时候你所拿到的Interge对象在自增后内存地址已经发生了改变,所以你在用这个新对象唤醒其他线程时,会出现IllegalMonitorStateException异常,提示你不是这个对象的拥有者,不能够唤醒在这个对象上活动的线程。

解决方法:在使用整形数据自增并不会改变锁对象的内存地址---》使用整形数组,数组的对象是在堆内存中的,只要线程共享这个数组对象,对数组内部数据的操作并不会引起数组内存地址的改变,所以也就不会出现该问题。

 

package com.cooler.javase.ThreadHomework;

import java.util.ArrayList;
import java.util.List;

/*
*
1、使用生产者和消费者模式实现,交替输出:
   假设只有两个线程,输出以下结果:
      t1-->1
      t2-->2
      t1-->3
      t2-->4
      要求:必须交替,并且t1线程负责输出奇数。t2线程负责输出偶数。
      两个线程共享一个数字,每个线程执行时都要对这个数字进行:++
      [注]用Interge对象不可以,涉及到整形常量池的地址问题,自增会导致对象的内存地址发生改变,最后都找不到锁机制
* */
public class ThreadTest {
    public static void main(String[] args) {
        Integer[] integers = {1};

        //共享了这个数字
        Thread myThreadOdd = new Thread(new MyThreadOdd(integers));
        Thread myThreadEven = new Thread(new MyThreadEven(integers));
        myThreadOdd.setName("myThreadOdd");
        myThreadEven.setName("myThreadEven");
        myThreadOdd.start();
        myThreadEven.start();
    }

}
class MyThreadOdd implements Runnable{
    private Integer[] nums;

    public MyThreadOdd(Integer[] nums) {
        this.nums = nums;
    }

    @Override
    public void run() {
        while (true){
            synchronized (nums){
                if (nums[0]%2==0){
                    try {
                        nums.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"----->num:"+nums[0]++);
                nums.notify();
            }
        }
    }
}

class MyThreadEven implements Runnable{
    private Integer[] nums;

    public MyThreadEven(Integer[] nums) {
        this.nums = nums;
    }

    @Override
    public void run() {
        while (true){
            synchronized (nums){
                if (nums[0]%2==1){
                    try {
                        nums.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"----->num:"+nums[0]++);
                nums.notify();
            }
        }
    }
}

输出结果:myThreadOdd----->num:1
myThreadEven----->num:2
myThreadOdd----->num:3
myThreadEven----->num:4
myThreadOdd----->num:5
myThreadEven----->num:6
myThreadOdd----->num:7
myThreadEven----->num:8
myThreadOdd----->num:9
myThreadEven----->num:10
myThreadOdd----->num:11
myThreadEven----->num:12
myThreadOdd----->num:13
myThreadEven----->num:14
myThreadOdd----->num:15
myThreadEven----->num:16
myThreadOdd----->num:17

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值