在较早版本的JDK中使用synchronized来实现线程安全,但同时使得并发的线程变成顺序执行,对系统并发吞吐能力有极大影响,在JDK1.5以后可以对其进行优化了。
我们先来看看原始的synchronized的使用方法:
悲观锁:
public Object get(Object key) {
synchronized(map) {
if(map.get(key) == null) {
// set some values
}
return map.get(key);
}
}
乐观锁:
public Object get(Object key) {
Object val = null;
if((val = map.get(key) == null) {
// if map value is null, add lock measure
synchronized(map) {
if(val = map.get(key) == null) {
// set some value to map...
}
}
}
//get value
return map.get(key);
}
String.intern:
上述乐观锁中还是不能很好解决大量写冲突问题,比如一个用户必须先创建session,才能进行后续的操作,但由于网络原因创建用户session的请求和后续请求几乎同时达到,而并行线程可能会先处理后续请求。一般情况,需要对用户sessionMap加锁,比如上面的乐观锁。在这种场景下,使用String.inter()是一种更高效的办法,类 String 维护一个字符串池,当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串,可见,当String相同时,String.intern()总是返回同一个对象,因此就实现了对同一用户加锁。由于锁的粒度局限于具体用户,使系统获得了最大程度的并发:
public void doSomeThing(String uid) {
synchronized(uid.intern()) {
// ...
}
}
ConcurrentHashMap:
String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的Full GC。怎么样能控制锁的个数,同时减小粒度锁呢?Java ConcurrentHashMap提供了一种很好的方式,将需要加锁的对象分为多个bucket,每个bucket加一个锁,伪代码如下:
Map locks = new Map();
List lockKeys = new List();
for(int number : 1 - 10000) {
Object lockKey = new Object();
lockKeys.add(lockKey);
locks.put(lockKey, new Object());
}
public void doSomeThing(String uid) {
Object lockKey = lockKeys.get(uid.hash() % lockKeys.size());
Object lock = locks.get(lockKey);
synchronized(lock) {
// do something
}
}
上述的做法相当于由全局(只有一个bucket,这个bucket加了锁后,都需要等待)锁变成了一个细粒度(多个bucket,每个bucket加一个锁,即使某个bucket加锁,但是其他bucket并没有加锁,无需等待,这样提高了并发、吞吐量),当然在java.util.concurrent包中还包含一系列的锁来处理同步的问题,比如:ReadWriteLock接口、MuTex等,这些对象在实例化以后都有类似的锁请求、锁超时、锁中断、锁释放的方法来维护锁,当然MuTex还包括了可以在不同的方法中实现锁传递(当获得/释放锁操作不能在同一个方法或者代码块中进行时候,不能使用synchronized块,这就要使用Mutex),这种情况下,要求在持有当前节点锁的同时,获得下一个节点的锁,但是,在获得下一个节点的锁之后,就可以释放当前的锁了!
分享到:
相关推荐
主要介绍了Java 并发编程学习笔记之Synchronized底层优化的相关资料,主要包含了重量级锁,轻量级锁,偏向锁和其他优化等方面,有需要的小伙伴可以参考下
手把手视频详细讲解项目开发全过程,需要的小伙伴自行百度网盘下载,链接见附件,永久有效。 课程简介 JVM 是 Java 程序的运行环境,学习 JVM,方能了解 Java 程序是如何被执行的,为进一步...5. synchronized 优化
12丨多线程之锁优化(上):深入了解Synchronized同步锁的优化方法.html
从这两个核心方法(get/put)可以看出 1.8 中对大链表做了优化,修改为红黑树之后查询效率直接提高到了 O(logn)。 但是 HashMap 原有的问题也都存在,比如在并发场景下使用时容易出现死循环。 final HashMap, ...
超全的多线程与高并发的编程笔记,从JVM&JMM角度讲多线程,synchronized优化原理,AQS和线程池等等,需要的童鞋请自行下载!
synchronized优化:代码 部分 synchronized底层实现: 早期JDK中,synchronized是重量级的,即需要调用操作系统(OS)来申请锁。 后来改进了,有了锁的升级: Java虚拟机中并没有严格规定synchronized需要如何实现,...
Lock接口与synchronized关键字在Java并发编程中都是用于实现同步机制的...在性能方面,synchronized通常比Lock接口开销更小,因为它是JVM内置的支持,优化程度更高。然而,在高度竞争的并发场景下,Lock接口可能会表现
为了避免锁膨胀,Java提供了一种称为“偏向锁”的优化策略。偏向锁的主要目的是减少锁竞争,提高并发性能。当一个对象首次被创建时,JVM会自动为其分配偏向锁。此时,如果其他线程试图获取该对象的锁,JVM会将对象头...
使用内建锁(synchronized)进行同步,关键在于要获取指定锁对象monitor对象,当线程获取monitor后才能继续向下执行,否则就只能等待。这个获取过程是互斥的,即同一时刻只有一个线程能够获取到对象的monitor监视器...
主要介绍了Java中synchronized实现原理详解,涉及synchronized实现同步的基础,Java对象头,Monitor,Mark Word,锁优化,自旋锁等相关内容,具有一定借鉴价值,需要的朋友可以参考下。
极客时间 | Java核心技术36讲所谓锁的升级、降级,就是 JVM 优化 synchronized 运行的机制,当 JVM 检测到不同的竞争状况时,会自动切换
所谓锁的升级、降级,就是 JVM 优化 synchronized 运行的机制,当 JVM 检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级
Tomcat的三种模式及并发优化Tomcat的三种模式及并发优化
一. 加载 预加载:1.反射注解框架Reflect信息,在Application内多线程预加载至缓存。...2. 并发操作多用读写锁,少用synchronized,Android虚拟机Art直到Android6.0为止尚未对synchronized做CAS优化,而sy
这是一个关于多线程下的单例模式优化代码。public class Singleton { private static Singleton instance; private Singleton (){ } public static Singleton getInstance(){ //对获取实例的方法进行同步 if...
诺基亚移动无线参数详细解释 ...第二部分:网络参数优化 147 一. TCH掉话参数优化 148 二. TCH拥塞参数优化 158 三. SDCCH拥塞参数优化 164 四. HO失败率参数优化 168 五. BOOSTER专题 175 六. 微蜂窝专题 185
主要介绍了jvm细节探索之synchronized及实现问题分析,涉及synchronized的字节码表示,JVM中锁的优化,对象头的介绍等相关内容,具有一定借鉴价值,需要的朋友可以参考下。
当两个线程都想访问共享的易变变量时,这两个线程不仅必须使用同步,而且如果它们正在使用synchronized块,那么这些synchronized 块还必须使用同一个锁对象。本文还介绍了JVM对于竞争锁请求和非竞争锁请求有不同的...
重入锁ReentrantLock 相对来说是synchronized、Object.wait()和Object.notify()方法的替代品(或者说是增强版),在JDK5.0的早期版本,重入锁的性能远远好于...,JDK在synchronized上做了大量的优化...
synchronized,但从JDK6.0开始,JDK在synchronized上做了⼤量的优化,使得两者的性能差距并 不⼤。重⼊锁对逻辑控制的灵活性要远远好于synchronized。 之所以称之为重⼊锁,就是⼀个线程允许反复进⼊。当然,这⾥的...