《聊聊synchronized为什么无法锁住Integer》
假设并发环境下,业务代码中存在一些统计操作,为了保证线程安全,开发人员往往会对计数值进行加锁(synchronized),值得注意的是,直接对Integer类型进行加锁,似乎并不会达到预期效果,比如下面这段代码:
Integer num = new Integer(0); public void test() throws InterruptedException { final int THREAD_SIZE = 10; final int TASK_SIZE = 100000; final CountDownLatch latch = new CountDownLatch(THREAD_SIZE); for (int i = 0; i < THREAD_SIZE; i++) { new Thread() { public void run() { for (int j = 0; j < TASK_SIZE / THREAD_SIZE; j++) { synchronized (num) { num++; } } latch.countDown(); } }.start(); } latch.await(); System.out.println("num-->" + num); } |
上述代码示例中,总共有10个线程运行,每个线程执行次数为10000(taskSize/threadSize),但是实际程序输出结果却并非是10W次,或许有些同学会觉得诧异,在对计数值num(Integer)进行递增操作前,已经执行了加锁操作,为啥还是非线程安全。我们首先来看一下上述程序的线程堆栈信息:
上图中,每一个线程锁住的资源其实都并非是同一个,这就可以解释为什么对Integer类型进行加锁仍然是非线程安全的。
或许有同学会说,JavaAPI提供有线程安全的AtomicInteger为啥不用,尽管AtomicInteger是线程安全的,但是接下来我们还是要聊一聊为啥锁不住Integer等原始数据类型的封装类型。
JAVA5为原始数据类型提供了自动装/拆箱功能,假设对Integer进行递增/递减操作后,其实HashCode已经发生了变化,synchronized自然也不是同一个对象实例,Integer的源码,如下所示:
从源码中可以看出,当为Integer赋值数值在-128~127区间时,会从Integer中的一个Integer[]中获取一个缓存的Integer对象,而超出区间值得时候,每次都会new一个新的Integer对象,假设下述这段代码:
Integer num = new Integer(300); System.out.println("identityHashCode-->" + System.identityHashCode(num)); System.out.println("hashCode-->" + num.hashCode()); num = 300; System.out.println("identityHashCode-->" + System.identityHashCode(num)); System.out.println("hashCode-->" + num.hashCode()); |
实际程序输出为:
identityHashCode-->627248822 hashCode-->300 identityHashCode-->523481450 hashCode-->300 |
Synchronized锁的是对象,也就是identityHashCode所指向的内存地址中的对象实例(根据对象内存地址生成散列值),而hashcode输出的是值得散列值。所以为啥上述程序示例中,identityHashCode每次不同,而hashCode输出的值却相同。
最后总结下,synchronized(Integer)时,当值发生改变时,基本上每次锁住的都是不同的对象实例,想要保证线程安全,推荐使用AtomicInteger之类会更靠谱。
相关推荐
java锁机制Synchronized java锁机制Synchronized java锁机制Synchronized java锁机制Synchronized
Synchronized锁在Spring事务管理下,导致线程不安全。
基于Synchronized加锁的四个demo,主要反映类锁、对象锁、方法锁的使用方法。 并测试不同类型锁之间的有效关系。
java锁机制Synchronized.pdf
lock锁,lock锁和synchronized的对比 # Lock锁 JDK5.0后Java提供了一种更加强大的线程同步机制。一种显式定义同步锁对象来实现锁,提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问...
主要介绍了Java并发 synchronized锁住的内容解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
由于对象头的信息是与对象自身定义的数据没有关系的额外存储成本,因此考虑到JVM的空间效率,Mark Word 被设计成为一个非固定的数据结构,以便存储更多有效的数据,它会根据对象本身的状态复用自己的存储空间,如32...
主要介绍了Java中Synchronized(对象锁)和Static Synchronized(类锁)的区别,希望对大家有所帮助,一起跟随小编过来看看吧
synchronized关键字”锁”的实现本质 synchronized关键字实现的锁是依赖于JVM的,...上面这段代码,synchronized锁的其实是 o 这个对象,不是{}里面的代码块,synchronized (o)加锁的过程可以理解为,通过改变o这个对
java代码-证明synchronized可重入锁
测试java中synchronized到底锁住了什么,完整demo
2. 聊聊并发(二)Java SE1.6中的Synchronized 3. 聊聊并发(三)Java线程池的分析和使用 4. 聊聊并发(四)深入分析ConcurrentHashMap 5. 聊聊并发(五)原子操作的实现原理 6. 聊聊并发(六)...
声明周期,旦置为nil之后就法做代码同步了。SyncData* data = id2data(obj, RELEASE);}}// @synchronized
volatile与synchronized的区别,锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)
在Java中,synchronized关键字是用来控制线程同步的,是在多线程的环境下,控制synchronized代码段不被多个线程同时执行。Synchronized既可以对代码块使用,也可以加在整个方法上。 关键是,不要认为给方法或者...
主要介绍了解析Java编程之Synchronized锁住的对象,具有一定参考价值,需要的朋友可以了解下。
java锁机制Synchronized[归纳].pdf
java高并发开发必须要会的知识,锁知识,关键字 Synchronized_锁升级知识点总结,面试常用