本文主要讲并行优化的几种方式, 其结构如下:

锁优化

减少锁的持有时间

例如避免给整个方法加锁

1 public synchronized void syncMethod(){ 2 othercode1(); 3 mutextMethod(); 4 othercode2(); 5 }

改进后

1 public void syncMethod2(){ 2 othercode1(); 3 synchronized(this){ 4 mutextMethod(); 5 } 6 othercode2(); 7 }

减小锁的粒度

将大对象,拆成小对象,大大增加并行度,降低锁竞争. 如此一来偏向锁,轻量级锁成功率提高.

一个简单的例子就是jdk内置的ConcurrentHashMap与SynchronizedMap.

Collections.synchronizedMap

其本质是在读写map操作上都加了锁, 在高并发下性能一般.

ConcurrentHashMap

内部使用分区Segment来表示不同的部分, 每个分区其实就是一个小的hashtable. 各自有自己的锁.

只要多个修改发生在不同的分区, 他们就可以并发的进行. 把一个整体分成了16个Segment, 最高支持16个线程并发修改.

代码中运用了很多volatile声明共享变量, 第一时间获取修改的内容, 性能较好.

读写分离锁替代独占锁

顾名思义, 用ReadWriteLock将读写的锁分离开来, 尤其在读多写少的场合, 可以有效提升系统的并发能力.

读-读不互斥:读读之间不阻塞。

读-写互斥:读阻塞写,写也会阻塞读。

写-写互斥:写写阻塞。

锁分离

在读写锁的思想上做进一步的延伸, 根据不同的功能拆分不同的锁, 进行有效的锁分离.

一个典型的示例便是LinkedBlockingQueue,在它内部, take和put操作本身是隔离的,

有若干个元素的时候, 一个在queue的头部操作, 一个在queue的尾部操作, 因此分别持有一把独立的锁.

1 /** Lock held by take, poll, etc */ 2 private final ReentrantLock takeLock = new ReentrantLock(); 3 4 /** Wait queue for waiting takes */ 5 private final Condition notEmpty = takeLock.newCondition(); 6 7 /** Lock held by put, offer, etc */ 8 private final ReentrantLock putLock = new ReentrantLock(); 9 10 /** Wait queue for waiting puts */11 private final Condition notFull = putLock.newCondition();

锁粗化

通常情况下, 为了保证多线程间的有效并发, 会要求每个线程持有锁的时间尽量短,

即在使用完公共资源后, 应该立即释放锁. 只有这样, 等待在这个锁上的其他线程才能尽早的获得资源执行任务.

而凡事都有一个度, 如果对同一个锁不停的进行请求 同步和释放, 其本身也会消耗系统宝贵的资源, 反而不利于性能的优化

一个极端的例子如下, 在一个循环中不停的请求同一个锁.

1 for(int i = 0; i

锁粗化与减少锁的持有时间, 两者是截然相反的, 需要在实际应用中根据不同的场合权衡使用.

JDK中各种涉及锁优化的并发类可以看之前的博文:并发包总结

ThreadLocal

除了控制有限资源访问外, 我们还可以增加资源来保证对象线程安全.

对于一些线程不安全的对象, 例如SimpleDateFormat, 与其加锁让100个线程来竞争获取,

不如准备100个SimpleDateFormat, 每个线程各自为营, 很快的完成format工作.

示例

1 public class ThreadLocalDemo { 2 3 public static ThreadLocal threadLocal = new ThreadLocal(); 4 5 public static void main(String[] args){ 6 ExecutorService service = Executors.newFixedThreadPool(10); 7 for (int i = 0; i原理对于set方法, 先获取当前线程对象, 然后getMap()获取线程的ThreadLocalMap, 并将值放入map中.该map是线程Thread的内部变量, 其key为threadlocal, vaule为我们set进去的值.1 public void set(T value) {2 Thread t = Thread.currentThread();3 ThreadLocalMap map = getMap(t);4 if (map != null)5 map.set(this, value);6 else7 createMap(t, value);8 }对于get方法, 自然是先拿到map, 然后从map中获取数据.1 public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) 7 return (T)e.value; 8 } 9 return setInitialValue();10 }内存释放手动释放: 调用threadlocal.set(null)或者threadlocal.remove()即可自动释放: 关闭线程池, 线程结束后, 自动释放threadlocalmap.1 public class StaticThreadLocalTest { 2 3 private static ThreadLocal tt = new ThreadLocal(); 4 public static void main(String[] args) throws InterruptedException { 5 ExecutorService service = Executors.newFixedThreadPool(1); 6 for (int i = 0; i list = new ArrayList();38 39 BigMemoryObject() {40 for (int i = 0; i内存泄露内存泄露主要出现在无法关闭的线程中, 例如web容器提供的并发线程池, 线程都是复用的.由于ThreadLocalMap生命周期和线程生命周期一样长. 对于一些被强引用持有的ThreadLocal, 如定义为static.如果在使用结束后, 没有手动释放ThreadLocal, 由于线程会被重复使用, 那么会出现之前的线程对象残留问题,造成内存泄露, 甚至业务逻辑紊乱.对于没有强引用持有的ThreadLocal, 如方法内变量, 是不是就万事大吉了呢? 答案是否定的.虽然ThreadLocalMap会在get和set等操作里删除key 为 null的对象, 但是这个方法并不是100%会执行到.看ThreadLocalMap源码即可发现, 只有调用了getEntryAfterMiss后才会执行清除操作,如果后续线程没满足条件或者都没执行get set操作, 那么依然存在内存残留问题.1 private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal key) { 2 int i = key.threadLocalHashCode & (table.length - 1); 3 ThreadLocal.ThreadLocalMap.Entry e = table[i]; 4 if (e != null && e.get() == key) 5 return e; 6 else 7 // 并不是一定会执行 8 return getEntryAfterMiss(key, i, e); 9 }10 11 private ThreadLocal.ThreadLocalMap.Entry getEntryAfterMiss(ThreadLocal key, int i, ThreadLocal.ThreadLocalMap.Entry e) {12 ThreadLocal.ThreadLocalMap.Entry[] tab = table;13 int len = tab.length;14 15 while (e != null) {16 ThreadLocal k = e.get();17 if (k == key)18 return e;19 // 删除key为null的value20 if (k == null)21 expungeStaleEntry(i);22 else23 i = nextIndex(i, len);24 e = tab[i];25 }26 return null;27 }最佳实践不管threadlocal是static还是非static的, 都要像加锁解锁一样, 每次用完后, 手动清理, 释放对象.无锁与锁相比, 使用CAS操作, 由于其非阻塞性, 因此不存在死锁问题, 同时线程之间的相互影响,也远小于锁的方式. 使用无锁的方案, 可以减少锁竞争以及线程频繁调度带来的系统开销.例如生产消费者模型中, 可以使用BlockingQueue来作为内存缓冲区, 但他是基于锁和阻塞实现的线程同步.如果想要在高并发场合下获取更好的性能, 则可以使用基于CAS的ConcurrentLinkedQueue.同理, 如果可以使用CAS方式实现整个生产消费者模型, 那么也将获得可观的性能提升, 如Disruptor框架.

Java高并发之锁优化相关推荐

  1. 高并发之Linux优化

    高并发之Linux优化 影响并发的主要参数 net.ipv4.tcp_max_tw_buckets 该参数设置系统的TIME_WAIT的数量,如果超过默认值则会被立即清除 net.core.somax ...

  2. Java 线程安全与锁优化

    什么是线程安全? 线程安全经常会被各种行业大佬或者面试官大佬挂在嘴边,如何找到一个通俗易懂一点的方式解释线程安全呢,伟大的砖家给出了答案:如果一个对象可以安全的被多个对象使用,那它就是线程安全的.是的 ...

  3. Java并发编程-synchronized锁优化

    目录 1.小故事 2.轻量级锁 3.锁膨胀 4.自旋优化 5.偏向锁 5.1.概述 5.2.偏向锁状态 5.3.偏向锁撤销 5.3.1.调用对象hashCode 5.3.2.其它线程使用对象 5.3. ...

  4. java线程安全和锁优化

    面向对象的编程思想是站在现实世界的角度去抽象和解决问题,他把数据和行为都看作是对象的一部分,这样可以让程序员能以符合现实世界的思维方式来编写和组织程序. 线程安全的一个恰当的定义:当多个线程访问一个对 ...

  5. Java 多线程编程(锁优化)

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

  6. 阅读笔记一——java高并发的性能优化

    前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...

  7. java 并发 调优_阅读笔记一——java高并发的性能优化

    前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...

  8. Java高并发之Hosee博客内容整理

    Hosee博客博客高并发系列目录 [高并发Java 一] 前言 [高并发Java 二] 多线程基础 [高并发Java 三] Java内存模型和线程安全 [高并发Java 四] 无锁 [高并发Java ...

  9. Java高并发之设计模式,设计思想

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:大道方圆 cnblogs.com/xdecode/p/913 ...

最新文章

  1. NeurIPS 2019 | 17篇论文,详解图的机器学习趋势
  2. 讲一讲应用服务的新鲜事儿
  3. 计算机网络(二十三)-网络层-概述与数据交换方式
  4. Windows下断言的类型及实现
  5. SEH in ASM 研究(一)
  6. php自学建议_如何高效自学PHP?
  7. baidu收录速度明显慢了
  8. 修改Windows登陆时显示上一次登陆的用户名
  9. 大龄程序员都去哪了?
  10. C# 报表设计器 (winform 设计端)开发与实现生成网页的HTML报表 开放源码及调试
  11. 导出android app安装包,Android app导出apk方法
  12. Sipeed MaixSense:Allwinner R329 (一)官方Debian系统--AIPU的基本使用--图像识别
  13. 学习数据结构的意义和作用
  14. 2022年最新江苏建筑特种工(施工升降机)模拟题库及答案
  15. 超详细的AD8031ARZ介绍,就在这里
  16. 2022-2028年中国植物工厂行业发展动态及投资规划分析报告
  17. mac for smartSVN9 (8,9)破解方法 附smartSvn_keygen工具图文
  18. SVM学习笔记——SVM解决多分类问题的方法
  19. 欧姆龙温控器参数笔记(三)(操作菜单)
  20. mMED影响组蛋白甲基化和表观遗传

热门文章

  1. 福建师范大学计算机考研好考吗,福建师范大学考研难吗?一般要什么水平才可以进入?...
  2. 路由交换以及其他网络名词基本概念
  3. 手工备份与还原Windows8激活文件
  4. Source Insight学习教程
  5. linux内存管理分析 二,linux内存管理分析【二】
  6. range函数python3_Python3如何使用range函数替代xrange函数
  7. resnet网络结构_深度学习之16——残差网络(ResNet)
  8. wolive-在线客服系统源码_如何搭建在线客服系统?
  9. build 之前执行task_Android Gradle新增buildtypes以及编译前执行自定义task
  10. php try catch 作用域,php作用域