《聊聊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之类会更靠谱。
相关推荐
当一个线程进入一个由synchronized修饰的方法或代码块时,其他线程将无法同时访问同一方法或代码块。Synchronized具有自动释放锁的特点,当线程执行完毕或遇到异常时,锁会自动释放,无需程序员显式调用解锁操作。...
为什么使用 Synchronized 代码块 使用 Synchronized 代码块可以缩小同步的影响范围,提高程序的运行效率。同时,Synchronized 代码块也可以在一定时期内霸占某个对象的钥匙,避免其他线程访问该对象的上锁房间。 ...
Java并发synchronized锁住的内容解析 Java并发synchronized锁住的内容解析是Java并发编程中的一种重要机制,用于解决多线程并发访问同一个资源时可能出现的线程安全问题。通过使用synchronized关键字,可以锁住当前...
Synchronized锁在Spring事务管理下,导致线程不安全。
本文将深入探讨`synchronized`关键字的用法,包括类锁、对象锁、方法锁以及它们之间的关系。 **类锁(Class Lock)** 类锁是通过类的Class对象实现的,当一个线程访问类的静态 synchronized 方法或同步代码块时,就...
2. **轻量级锁**:当偏向锁无法满足多线程并发时,会转变为轻量级锁。轻量级锁使用CAS(Compare and Swap,比较并交换)操作来尝试获取锁,如果成功,就继续执行;如果失败,说明已有其他线程持有锁,这时轻量级锁...
java锁机制Synchronized.pdf
Java synchronized锁住的对象解析 在Java编程中,synchronized关键字是用于同步的关键字,它可以用于锁住一个对象,以实现线程同步。但是,在使用synchronized关键字时,需要了解锁住的到底是哪个对象,否则可能...
lock锁,lock锁和synchronized的对比 # Lock锁 JDK5.0后Java提供了一种更加强大的线程同步机制。一种显式定义同步锁对象来实现锁,提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问...
Java 中 Synchronized(对象锁)和 Static Synchronized(类锁)的区别 Synchronized 和 Static Synchronized 是 Java 中两种同步机制,它们都用于解决多线程并发访问的安全问题。然而,它们之间存在着本质的区别。...
在Objective-C中,当你使用`@synchronized`块包围一段代码时,会为指定的对象创建一个互斥锁。如果当前线程已经持有了该锁,那么它可以再次获取并执行内部的代码,而不会造成死锁。这种特性使得在嵌套使用`@...
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
但如果自旋超过了预定次数,或者锁竞争激烈,线程可能无法在短时间内获得锁,此时轻量级锁会升级为重量级锁。 重量级锁是阻塞锁的一种,在获取锁失败后,线程会被阻塞挂起,直到锁被释放,其他线程才有机会获得锁。...
synchronized() 中是锁住的对象,synchronized(this) 锁住的只是对象本身,同一个类的不同对象调用这个方法并不会被锁住,而 synchronized(className.class) 实现了全局锁的功能,全部这个类的对象调用这个方法都受...
《深入理解synchronized锁自旋机制》 在Java并发编程中,`synchronized`关键字扮演着重要的角色,它提供了一种线程同步机制,确保共享资源在同一时刻只能被一个线程访问。本文将深入探讨`synchronized`锁的内部机制...
在上面的例子中,MySubClass 中的 myMethod 方法不自动变成 synchronized 方法,需要显式地指定它为 synchronized 方法。 四、synchronized 关键字的使用示例 以下是一个简单的示例代码,演示了 synchronized ...
4. **锁膨胀**:如果同步代码块的性能瓶颈导致CPU开销过大,JVM会将轻量级锁升级为重量级锁(监视器锁),这可能导致性能下降。 四、`synchronized`与其他并发工具类的对比 1. **ReentrantLock**:具有`...
Synchronized 关键字的作用是取得对象的锁,而不是把一段代码或函数当作锁。每个对象只有一个锁与之相关联。实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 当 ...
标题中的"java 多线程synchronized互斥锁demo"指的是一个示例,展示了如何在多线程环境下使用`synchronized`关键字创建互斥锁,确保同一时间只有一个线程可以访问特定的代码块或方法。 描述中的"一个多线程访问的同...