Java锁优化

应用程序在并发环境下会产生很多问题,通常情况下,我们可以通过加锁来解决多线程对临界资源的访问问题。但是加锁往往会成为系统的瓶颈,因为加锁和释放锁会涉及到与操作系统的交互,会有很大的性能问题。那么这个时候基于锁的优化手段就显得很重要了。

一般情况下,可以从两个角度进行锁优化:对单个锁算法的优化和对锁粒度的细分。

1. 单个锁的优化

自旋锁:

​ 非自旋锁在未获取锁的情况会被阻塞,之后再唤醒尝试获得锁。而JDK的阻塞和唤醒是基于操作系统实现的,会有系统资源的开销。自旋锁就是线程不停地循环尝试获得锁,而不会将自己阻塞,这样不会浪费系统的资源开销,但是会浪费CPU的资源。所有现在的JDK大部分都是先自旋等待,如果自旋等待一段时间之后还没有获取到锁,就会将当前线程阻塞。

锁消除:

​ 当JVM分析代码时发现某个方法只被单个线程安全访问,而且这个方法是同步方法,那么JVM就会去掉这个方法的锁。

单个锁优化的瓶颈:

​ 对单个锁优化的效果就像提高单个CPU的处理能力一样,最终会由于各个方面的限制而达到一个平衡点,到达这个点之后优化单个锁的对高并发下面锁的优化效果越来越低。所以将一个锁进行粒度细分带来的效果会很明显,如果一个锁保护的代码块被拆分成两个锁来保护,那么程序的效率就大约能够提高到2倍,这个比单个锁的优化带来的效果要明显很多。常见的锁粒度细分技术有:锁分解和锁分段

2. 细分锁粒度

细分锁粒度的目的是降低竞争锁的概率。

2.1 锁分解

锁分解的核心是将无关的代码块,如果在一个方法中有一部分的代码与锁无关,一部分的代码与锁有关,那么可以缩小这个锁的返回,这样锁操作的代码块就会减少,锁竞争的可能性也会减少

缩小锁的范围

缩小锁的范围是指尽量只在必要的地方加锁,不要扩大加锁的范围,就拿单例模式举例,范围大的锁可能将整个方法都加锁了:

class Singleton {
private Singleton instance; private Singleton() {
} // 将整个方法加锁
public synchronized Singleton getInstance() {
try {
Thread.sleep(1000); //do something
if(null == instance)
instance = new Singleton();
} catch (InterruptedException e) {
e.printStackTrace();
} return instance;
} }

优化后的,只将部分代码加锁:

class Singleton {
private Singleton instance; private Singleton() {
} public Singleton getInstance() {
try {
Thread.sleep(1000); //do something
// 只对部分代码加锁
synchronized(this) {
if(null == instance)
instance = new Singleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
} return instance;
}
}

减少锁的粒度

减少锁的粒度是指如果一个锁需要保护多个相互独立的变量,那么可以将一个锁分解为多个锁,并且每个锁保护一个变量,这样就可以减少锁冲突。看一下下面的例子:

class Demo{
private Set<String> allUsers = new HashSet<String>();
private Set<String> allComputers = new HashSet<String>(); //公用一把锁
public synchronized void addUser(String user){
allUsers.add(user);
} public synchronized void addComputer(String computer){
allComputers.add(computer);
}
}

缩小锁的粒度后,将一个锁拆分为多个:

class Demo{
private Set<String> allUsers = new HashSet<String>();
private Set<String> allComputers = new HashSet<String>(); //分解为两把锁
public void addUser(String user){
synchronized (allUsers){
allUsers.add(user);
}
} public void addComputer(String computer){
synchronized (allComputers){
allComputers.add(computer);
}
}
}

如上的方法把一个锁分解为2个锁时候,采用两个线程时候,大约能够使程序的效率提升一倍。

2.2 锁分段

锁分段和缩小锁的粒度类似,就是将锁细分的粒度更多,比如将一个数组的每个位置当做单独的锁。JDK8以前ConcurrentHashMap就使用了锁分段技术,它将散列数组分成多个Segment,每个Segment存储了实际的数据,访问数据的时候只需要对数据所在的Segment加锁就行。

参考:

Java锁分解锁分段技术: http://guochenglai.com/2016/06/04/java-concurrent4-java-subsection-decompose/

ConcurrentHashMap的锁分段技术:https://blog.csdn.net/yansong_8686/article/details/50664351

Java锁优化的更多相关文章

  1. Java 锁优化

    一.重量级锁   Java中,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的.但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的.而操作系统实现 ...

  2. 深入理解java虚拟机(7)---线程安全 & 锁优化

    关于线程安全的话题,足可以使用一本书来讲解这些东西.<Java Concurrency in Practice> 就是讲解这些的,在这里 主要还是分析JVM中关于线程安全这块的内容. 1. ...

  3. Java的锁优化

    高效并发是从JDK 1.5到JDK 1.6的一个重要改进,HotSpot虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术,如适应性自旋(Adaptive Spinning).锁消除(Lo ...

  4. java多线程编程——锁优化

    并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如 ...

  5. Java并发编程:synchronized和锁优化

    1. 使用方法 synchronized 是 java 中最常用的保证线程安全的方式,synchronized 的作用主要有三方面: 确保线程互斥的访问代码块,同一时刻只有一个方法可以进入到临界区 保 ...

  6. 深入介绍Java中的锁[原理、锁优化、CAS、AQS]

    1.为什么要用锁? 锁-是为了解决并发操作引起的脏读.数据不一致的问题. 2.锁实现的基本原理 2.1.volatile Java编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新, ...

  7. 深入理解Java虚拟机读书笔记9----线程完全与锁优化

    九 线程完全与锁优化   1 Java语言中的线程完全         ---线程安全:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用 ...

  8. Java 虚拟机对锁优化所做的努力

    作为一款公用平台,JDK 本身也为并发程序的性能绞尽脑汁,在 JDK 内部也想尽一切办法提供并发时的系统吞吐量.这里,我将向大家简单介绍几种 JDK 内部的 "锁" 优化策略. 1 ...

  9. Java并发编程学习:线程安全与锁优化

    本文参考<深入理解java虚拟机第二版> 一.什么是线程安全? 这里我借<Java Concurrency In Practice>里面的话:当多个线程访问一个对象,如果不考虑 ...

随机推荐

  1. Xmind ZEN破解版来袭:如何去除水印

    Xmind ZEN是一款十分优雅地思维导图软件,但是找不到其破解版,在导出图片时就会携带上水印. image-20190110110013642.png 当然,土豪请(点击这里关闭). image-2 ...

  2. tr 命令详细介绍

    tr用来从标准输入中对字符进行操作,主要用于删除文件中指定字符.字符转换.压缩文件字符. 我们可以用:tr --help查看一下系统详细介绍 [root@bqh-118 scripts]# tr -- ...

  3. 【Zookeeper】 在Java中的操作

    一.基本功能演示 1.1 Maven依赖信息 1.2 代码演示 方法说明 1.3 创建Zookeeper节点信息 二.Watcher 2.1 什么是Watcher接口 2.2 Watcher代码 一. ...

  4. Ubuntu 文件和目录常用命令

    目标 查看目录内容 ls 切换目录 cd 创建和删除操作 touch rm mkdir 拷贝和移动文件 cp mv 查看文件内容 cat more grep 其他 echo 重定向 > 和 &g ...

  5. Linux网络管路——网络相关命令ping、traceroute

    ping [root@51cto /]# ping www.baidu.com PING www.a.shifen.com (() bytes of data. bytes from ttl= tim ...

  6. 排序方法——python

    1.冒泡排序法(Bubble Sort) 比较相邻的元素.如果第一个比第二个大,就交换它们两个: 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数: 针对所 ...

  7. 第一课 IP通信

       我们的专业课:<IP通信>开课了.    在第一节课,我们初步了解了关于通信的知识,涨了知识,下面我就说一下第一节课所学所感.    在学习这门课的时候,需要我们认真预习,认真听课, ...

  8. 如何利用while语句打印“九九乘法口诀表”

    需求:输出九九乘法表 plus.py代码如下: i=1 j=1 while i<=9: j=1 while j<=i: print(j,'*',i,'=',str(i*j)+' ',end ...

  9. Disable trigger to avoid the ID is auto-updated

    CREATE TABLE COBRA.COBRA_PRODUCT_INFO_BAK AS SELECT * FROM COBRA.COBRA_PRODUCT_INFO; TRUNCATE TABLE ...

  10. python3 基础二——基本的数据类型一

    一.基本的数据类型 Python3 中有六个标准的数据类型Number(数字). String(字符串). List(列表) .Tuple(元组). Sets(集合) .Dictionary(字典) ...