转载自 java并发编程之4——Java锁分解锁分段技术

并发编程的所有问题,最后都转换成了,“有状态bean”的状态的同步与互斥修改问题。而最后提出的解决“有状态bean”的同步与互斥修改问题的方案是为所有修改这个状态的方法都加上锁,这样也就可以保证他们在修改bean的状态的时候是顺序进行的。但是这样整个过程的瓶颈也就是被加锁的这段代码。由此就产生了很多对程序加锁的优化思想,从整体上来看,可以分为两个部分:对单个锁的算法的优化。和对锁粒度的细分。

单个锁的优化

自旋锁:非自旋锁在未获取锁的情况会被阻塞,之后再唤醒尝试获得锁。而JDK的阻塞和唤醒是基于操作系统实现的,会有系统资源的开销。自旋锁就是线程不停地循环尝试获得锁,而不会将自己阻塞,这样不会浪费系统的资源开销,但是会浪费CPU的资源。所有现在的JDK都的是先自旋等待,如果自旋等待一段时间之后还没有获取到锁,就会将当前线程阻塞。
      锁消除:当JVM分析代码发现某个方法只被单个线程安全访问,而且这个方法是同步方法,那么JVM就会去掉这个方法的锁。
单个锁优化的瓶颈
      对单个锁优化的效果就像提高单个CPU的处理能力一样,最终会由于各个方面的限制而达到一个平衡点,到达这个点之后优化单个锁的对高并发下面锁的优化效果越来越低。所以将一个锁进行粒度细分带来的效果会很明显,如果一个锁保护的代码块被拆分成两个锁来保护,那么程序的效率就大约能够提高到2倍,这个比单个锁的优化带来的效果要明显很多。常见的 锁粒度细分技术有:锁分解和锁分段

锁粒度细分

锁的粒度细分主要有:锁分解和锁分段两种方式。他们的核心都是降低锁竞争发生的可能性。

锁分解

锁分解的实现方式主要有两种:

缩小锁的范围

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

传统写法:

public synchronized void synchronizedOnMethod(){ //粗粒度直接在方法上加synchronized,这样会提高锁冲突的概率prefix();try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}post();}private void post(){try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}}private void prefix(){try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}}}

修正写法

//假设prefix和post方法是线程安全的(与锁无关的代码)
static class SynchronizedClazz{public void mineSynOnMethod(){prefix();synchronized (this){ //synchronized代码块只保护有竞争的代码try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){}}post();}

减少锁的粒度

如果一个锁需要保护多个相互独立的变量,那么可以将一个锁分解为多个锁,并且每个锁保护一个变量,比较对比下面的两段代码,

假设 allUsers和allComputers是两个相互独立的变量

传统写法

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

修正写法

static class DecompossClazz2{private final Set<String> allUsers = new HashSet<String>();private final 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个锁时候,采用两个线程时候,大约能够使程序的效率提升一倍,但是当竞争激烈的时候,单一个锁上面的竞争还是很激烈,我们还可以将锁分解技术进一步扩展到一组独立的对象例如ConcurrentHashMap的锁分段技术

package com.qunar.des.lock;
import java.util.HashMap;
import java.util.Map;
/*** Created by chenglaiguo on 8/14/15.*/
public class MyConcurrentHashMap<K,V> {private final int LOCK_COUNT = 16;private final Map<K,V> map;private final Object[] locks ;public MyConcurrentHashMap() {this.map = new HashMap<K,V>();locks = new Object[LOCK_COUNT];for (int i=0;i<LOCK_COUNT;i++){locks[i] = new Object();}}private int keyHashCode(K k){return Math.abs(k.hashCode() % LOCK_COUNT);}public V get(K k){int keyHashCode = keyHashCode(k);synchronized (locks[keyHashCode % LOCK_COUNT]){return map.get(k);}}}

java并发编程之4——Java锁分解锁分段技术相关推荐

  1. zbb20180929 thread java并发编程之Condition

    java并发编程之Condition 引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout ...

  2. Java并发编程之CAS第三篇-CAS的缺点

    Java并发编程之CAS第三篇-CAS的缺点 通过前两篇的文章介绍,我们知道了CAS是什么以及查看源码了解CAS原理.那么在多线程并发环境中,的缺点是什么呢?这篇文章我们就来讨论讨论 本篇是<凯 ...

  3. Java并发编程之CyclicBarrier详解

    简介 栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生.栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行.闭锁用于等待事件,而栅栏用于等待其他线程. CyclicBarrier ...

  4. Java 并发编程之美:并发编程高级篇之一-chat

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  5. Java 并发编程之美:并发编程高级篇之一

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  6. 深入理解并发编程之CAS无锁机制与ABA问题

    深入理解并发编程之CAS无锁机制与ABA问题 文章目录 深入理解并发编程之CAS无锁机制与ABA问题 前言 一.什么是CAS无锁机制 二.CAS原理分析 1.AtomicLong自增分析 2.基于At ...

  7. Java并发编程之Lock

    Java并发编程:Lock Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.uti ...

  8. Java并发编程之synchronized关键字解析

    前言 公司加班太狠了,都没啥时间充电,这周终于结束了.这次整理了Java并发编程里面的synchronized关键字,又称为隐式锁,与JUC包中的Lock显示锁相对应:这个关键字从Java诞生开始就有 ...

  9. java并发编程之AbstractQueuedSynchronizer

    引言 AbstractQueuedSynchronizer,队列同步器,简称AQS,它是java并发用来构建锁或者其他同步组件的基础框架. 一般使用AQS的主要方式是继承,子类通过实现它提供的抽象方法 ...

最新文章

  1. 从ViewDragLayout中学到的一些事实
  2. Tensorflow快餐教程(8) - 深度学习简史
  3. 建立Windows Embedded Compact 7开发环境
  4. Source Insight,修改字体
  5. 计算机专业在湖南录取分数,计算机科学与技术专业分数线各大学排名(湖南)
  6. phpMyAdmin提示:配置文件权限错误,无法写入!解决方法
  7. 复杂网络代码_据报道称“浏览器内核有上千万行代码”,浏览器内核真的很复杂吗?...
  8. 昆仑通态复制的程序可以用吗_MCGS昆仑通态触摸屏常见问题(5)
  9. 计算机毕业论文中期论文质量,计算机 毕业论文(设计)中期报告(1页)-原创力文档...
  10. 做一个略调皮的个人博客--菜单篇
  11. PHP 梯形图,初学者必掌握plc梯形图解释
  12. flutter 返回键监听
  13. 让天嵌的E9板通过网线与能上网的笔记本连接实现联网
  14. 故事与她用计算机打,「故事」他用笔和相机为警察“代言”!(856)
  15. python 条形图填充疏密_教你利用Python玩转histogram直方图的五种方法
  16. 【文献翻译】软件设计级漏洞分类模型-Software Design Level Vulnerability Classification Mode
  17. 推动RISC-V拾级而上 赛昉科技发布两款高性能产品: JH7110多媒体处理器与VisionFive 2开发板
  18. token是什么?token的作用以及运用场景?
  19. 有没有比较好的网页整站下载工具?
  20. 我国超级计算机第一名是,中国蝉联超级计算机冠军,美国跌出前三

热门文章

  1. Pytorch中的 torch.as_tensor() 和 torch.from_numpy() 的区别
  2. 手把手教你剖析vue响应式原理,监听数据不再迷茫
  3. 图的遍历(C语言,邻接表存储的图 - DFS,邻接矩阵存储的图 - BFS)
  4. C++变量的初始化问题及列表初始化
  5. 二叉树的中序遍历非递归方法(算法导论第三版12.1-3)
  6. 题目 1885: [蓝桥杯][2017年第八届真题]分巧克力+二分
  7. 迁移到其他机器_有赞大数据离线集群迁移实战
  8. 哈希表创建哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构.typedef enum{ HASH_OK-icoding-数据结构-C
  9. Linear Programming_the simplex method in tableau format
  10. Ancient Distance(妙啊!!!) [2020牛客暑期多校训练营(第四场)]