用Integer当作锁的时候的注意事项

前几天看到一个经典的面试题

交替输出 A B

我写了这么一版本

package com.jiubodou.leetcode.经典150.生产者消费者问题.第六次变种;

/**
 * @ClassName Test
 * @Description 探究一下 Integer a =0 ; a++ 的问题
 * @Author huyingliang
 * @Date 2023/7/28 10:08
 */
public class Test {
    static Integer a = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronized (a) {
                    if (a % 2 == 0) {
                        System.out.println("A");
                        a++;
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronized (a) {
                    if (a % 2 == 1) {
                        System.out.println(a);
                        System.out.println("B");
                        a++;
                        System.out.println(a);
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }).start();

//        A
//        B
//        Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
//        at java.base/java.lang.Object.notifyAll(Native Method)
//        at com.jiubodou.leetcode.经典150题.生产者消费者问题.第六次变种.Test.lambda$main$1(Test.java:51)
//        at java.base/java.lang.Thread.run(Thread.java:834)
//        java.lang.IllegalMonitorStateException
//        at java.base/java.lang.Object.notifyAll(Native Method)
//        at com.jiubodou.leetcode.经典150题.生产者消费者问题.第六次变种.Test.lambda$main$0(Test.java:26)
//        at java.base/java.lang.Thread.run(Thread.java:834)

        /*报错的内容是  在没有锁的情况下 使用了 notifyAll notify 或者 wait方法
         * 可是可以看到 20 和 44 都是拿到锁的,怎么突然就又没拿到锁了呢
         * 问题就出在 a++ 这个语句上 ,他的原型是 a=a+1   如果 a=a+1 执行完成后 ,a的地址变了的话
         * 那么就会出现这个报错。那么接下来就验证一下 是不是真的变了  在java中不太容易查看 但是debug的时候可以查看
         * 46 行 49行 打上断点之后就可以查看  a  的地址了
         * 实际上 在第13行的时候,如果写成      static final Integer a = 0;  就会发现 a++ 是会报错的
         * 这也印证了,a++ 其实是会换地址的*/
    }
}

下面使用另外一种方式

package com.jiubodou.leetcode.经典150.生产者消费者问题.第六次变种;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName Test
 * @Description 两个线程交替打印AB 这里一定要用 AtomicInteger 不能用 Integer
 * @Author huyingliang
 * @Date 2023/7/28 10:08
 */
public class Test2 {
    static final AtomicInteger a = new AtomicInteger(0);
    public static void main(String[] args) {
        System.out.println("a.get() = " + a.get());
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronized (a) {
                    if (a.get() % 2 == 0) {
                        System.out.println("A");
                        a.incrementAndGet();
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }

                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronized (a) {

                    if (a.get() % 2 == 1) {
                        System.out.println("B");
                        a.incrementAndGet();
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        a.notifyAll();
                        try {
                            a.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }

                }
            }
        }).start();
    }
}

使用 AtomicInteger 就可以了
看一下源码会发现:
用Integer当作锁的时候的注意事项_第1张图片
这里记录了对象的地址在值变化的时候就会使用到这里的原对象的地址,所以说地址最终是不会变的。

你可能感兴趣的:(java,面试,开发语言)