synchoronized 为什么不能锁 int 或者 long 类型

文章探讨了Java中`synchronized`关键字不能直接用于整型(int和long)的原因,因为它们会被转换为新的对象,导致锁失效。文章通过示例和源码分析揭示了IntegerCache和LongCache的作用,并提到了阿里巴巴开发规范中的相关规则。
摘要由CSDN通过智能技术生成

前言

synchronized(Object)
不能用String常量
因为字符串常量是在内存中同一个地方。如果两个线程锁一个,可能导致死锁。一个线程多次可能导致重入。

也不能用Integer,Long等基础类型。
因为当锁定int或者long时, jvm就会调用Interger.valueOf()生成一个新的对象,所以锁不会失效
看源码可知,java会把-128到127所对应的包装类都给缓存起来,分别存入了IntegerCache和LongCache
而当锁的值大于127之后,就会新建一个对象,在堆上产生,这时,锁就会失效。


synchoronized 为什么不能锁 int 或者 long 类型

1 测试代码

@Test
fun lockWithPrimitiveType() {

    val startTime = Instant.now()
    Thread {
        while (true) {
            synchronized(100L) {
                TimeUnit.SECONDS.sleep(2L) // 模拟业务代码耗时
                val endTime = Instant.now()
                val duration = Duration.between(startTime, endTime)
                println("current thread:${Thread.currentThread().name}, I am guild one, seconds: ${duration.seconds}")
            }
        }
    }.start()

    Thread {
        while (true) {
            synchronized(100L) {
                TimeUnit.SECONDS.sleep(2L) // 模拟业务代码耗时
                val endTime = Instant.now()
                val duration = Duration.between(startTime, endTime)
                println("current thread:${Thread.currentThread().name}, I am guild two, seconds: ${duration.seconds}")
            }
        }
    }.start()

    while (true) {}
}


让我们来看一下打印结果:

current thread:Thread-3, I am guild one, seconds: 2
current thread:Thread-4, I am guild two, seconds: 4
current thread:Thread-4, I am guild two, seconds: 6
current thread:Thread-4, I am guild two, seconds: 8
current thread:Thread-4, I am guild two, seconds: 10
current thread:Thread-4, I am guild two, seconds: 12
current thread:Thread-4, I am guild two, seconds: 14
current thread:Thread-4, I am guild two, seconds: 16
current thread:Thread-4, I am guild two, seconds: 18
current thread:Thread-4, I am guild two, seconds: 20

从日志来看,Thread-3线程在执行业务代码的时候,Thread-4线程并没有被执行(阻塞等待)。Thread-4线程在执行业务代码的时候, Thread-3线程也没有被执行。由此可见,锁已经生效了。于是得出了一个结论:“synchornized 锁一个 long 类型的整数是可行的”。
可事实是真是这样吗?
下面对代码做一个简单的修改,将 synchronzied(100L) 改为 synchronized(1000L),再进行测试。

@Test
fun lockWithPrimitiveType() {

    val startTime = Instant.now()
    Thread {
        while (true) {
            synchronized(1000L) { // 将 100L 改为 1000L
                TimeUnit.SECONDS.sleep(2L) // 模拟业务代码耗时
                ..... // 与之前代码一致
            } 
        }
    }.start()

    Thread {
        while (true) {
            synchronized(1000L) { // 将 100L 改为 1000L
                TimeUnit.SECONDS.sleep(2L) // 模拟业务代码耗时
                ...... // 与之前代码一致
            }
        }
    }.start()

    while (true) {}
}


让我们继续看一下打印结果:

current thread:Thread-4, I am guild two, seconds: 2
current thread:Thread-3, I am guild one, seconds: 2
current thread:Thread-4, I am guild two, seconds: 4
current thread:Thread-3, I am guild one, seconds: 4
current thread:Thread-4, I am guild two, seconds: 6
current thread:Thread-3, I am guild one, seconds: 6
current thread:Thread-3, I am guild one, seconds: 8
current thread:Thread-4, I am guild two, seconds: 8
current thread:Thread-4, I am guild two, seconds: 10
current thread:Thread-3, I am guild one, seconds: 10


这个时候,我们会发现,Thread-4线程在执行业务代码的时候,Thread-3线程同时也在执行业务代码,那么这个时候,锁为什么没有生效呢?
答案是:synchronized 只能锁对象,那么当我们传入 int 类型或者 long 类型整数的时候,java 会调用 Integer.valueOf() 或者 Long.valueOf() 方法将它们转为一个新的对象,所以锁不会生效。
那么为什么锁 100L 这个整数时,锁会生效,这个时候我们看源码可以得知,Java 会把 -128 到 127 所对应的包装类对象都给缓存起来,分别存入了 IntegerCache 和 LongCache 对象里。

2 源码分析

Integer.valueOf() 方法

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high) // 如果 i >= -128 且 i <= 127
        return IntegerCache.cache[i + (-IntegerCache.low)]; // 返回 IntegerCache 里的对象
    return new Integer(i); // 返回一个新对象
}


IntegerCache 类

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        int h = 127
        ...... // 省略非关键性代码
        heigh = h

        cache = new Integer[(high - low) + 1]; // 设置数组的大小(256)
        int j = low;
        for(int k = 0; k < cache.length; k++) // 为 -128 到 127 之间的整数创建 Integer 对象
            cache[k] = new Integer(j++); 
       ...... // 省略非关键性代码
    }

    private IntegerCache() {}
}

Long.valueOf() 方法

public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset]; // 返回 LongCache 里的对象
    }
    return new Long(l); // 返回新对象
}


LongCache 类

private static class LongCache {
    private LongCache(){}

    static final Long cache[] = new Long[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Long(i - 128);
    }
}

可以发现,Integer.valueOf() 与 Long.valueOf 实现方法略有不同,IntegerCache 和 LongCache 实现方法也略有不同,可是结果都是一样的:都是对-128 到 127 之间的整数所对应的包装类对象进行缓存。
为什么实现没有保持统一,我们也不得而知了,我猜可能是实现代码的作者不同吧,哈哈哈。

3 阿里巴巴开发规范

在阿里巴巴开发规范,OOP 规约里第 7 条也有提到这么一项的强制规则。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑白极客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值