并发设计模式和锁优化以及jdk8并发新特性
1 设计模式
(1) 单例模式
保证一个类只能一个对象实现。正常的单例模式分为懒汉式和饿汉式,饿汉式就是把单例声明称static a=new A(),系统第一次调用的时候生成(包括调用该类的其他静态资源也会生成),懒汉式就是系统调用get函数的时候,加个锁判断单例对象是否存在,存在就返回不存在就声明一个。好一点的懒汉式应该把单例加一个静态内部类,第一次访问的类的时候静态内部类不会初始化,当调用的get方法的时候再实例化,这样不用加锁效率高一些,
public class StaticSingleton {
private StaticSingleton(){
System.out.println("StaticSingleton is create");
}
private static class SingletonHolder {
private static StaticSingleton instance = new StaticSingleton();
}
public static StaticSingleton getInstance() {
return SingletonHolder.instance;
}
(2)不变模式 类和变量都声明为final,只要创建就不可变,常见的string Integer Double等都是不可变。
(3) future模式 客户端请求服务端数据,如果服务端处理时间较长,可以返回一个空值(类似代理),启动一个线程专门设值。客户端可以先干别的,当想要试用这个值时,可以从代理里拿,如果代理值已经设置好直接返回,如果没设置好则wait,等设置好了的时候notify。 可以向excutor里提交一个实现了Collable的对象,会返回一个Future,然后使用这个future.get()拿值。
(4) 生产者消费者模式 专门有生产者生产数据,消费者消费数据,中间靠线程安全的队列作为公共区域,各线程都从这个区域里写值和读值。各个线程无需了解对存在,只要负责自己的事情即可,也符合开闭原则。
2 锁优化
具体思路:减少锁持有时间,减小锁粒度,锁分离,锁粗化,锁消除。
(1)减少锁持有时间 尽量少的加锁代码,例如用具体代码段代替方法加锁。
(2)减小锁粒度 把大对象尽量改成小对象,增加并行度减少锁竞争。同时有利于偏向锁,轻量级锁。例如ConcurrentHashMap
(3)锁分离 读写分离,读读可重入,读写互斥,写写互斥。另一种分离,例如 LinkedBlockingQueue ,存数据和取数据从队列两端操作,两端各自加锁控制即可,两端的锁互不影响。
(4)锁粗化 如果一段程序要多次请求锁,锁之间的代码执行时间比较少,就应该整合成一个锁,前提是不用同步的部分执行时间短。例如for循环里面申请锁,如果for循环时间不长,可以在for外面加锁。
(5)锁消除 编译器级别的操作,如果jdk发现锁不可能被共享,会擦除这个锁。原理是逃逸分析,例如stringbuffer,本身操作是加锁的,如果只在局部使用不存在并发访问,那么会擦除锁,如果对象逃逸出去例如赋值给全局变量等,面临并发访问,就不会擦除锁。可以通过jvm参数来指定是否使用锁消除。
3 jdk的锁优化 sychronized的优化,由虚拟机完成
(1)偏向锁 在竞争比较少的情况下,会使用偏向锁来提高性能。
*对象头 markword,共32位,存hash,锁信息(指向锁的指针),垃圾回收标志(偏向锁id),年龄信息,偏向锁线程id,monitor信息等。
一个线程争取到对象资源时,对象会在对象头中标记为偏向,并且将线程id写入到对象头中,下次如果这个线程再来可以不通过锁竞争直接进入同步块。当其他线程访问的时候,偏向结束,升级为轻量级锁。所以在竞争激烈的场景下偏向锁会增加系统负担,jvm默认是开启偏向锁的,可以通过jvm参数设置取消偏向锁
*偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令,在只有一个线程执行同步块时进一步提高性能。
(2)轻量级锁 轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。
轻量级锁的加锁过程 :
1)在代码进入同步块的时候,如果同步对象锁状态为无锁状态(偏向锁也是无锁),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝。
2)拷贝对象头中的Mark Word复制到锁记录中。
3)拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock record里的owner指针指向object mark word。
4)如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为处于轻量级锁定状态。
5)如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁,自旋就是为了不让线程阻塞,而采用循环去获取锁的过程。
轻量级锁解锁时,把复制的对象头替换回去(cas)如果替换成功,锁结束,如果失败,说明有竞争,升级为重量级锁(先会自旋一下等等看),notify 唤醒其他等待线程。
* 轻量级锁是为了在线程交替执行同步块时提高性能。
(3)自旋锁
轻量级锁加锁失败以后,可能先自旋一段时间,尝试获得轻量级锁,不会着急升级为重量级锁挂起。如果自旋过多,会造成cpu资源浪费,JDK采用了适应性自旋,简单来说就是一开始设置固定自旋次数,线程如果自旋成功了,则下次自旋的次数会更多,如果自旋失败了,则自旋的次数就会减少。
*自旋如果成功,可以省略线程挂起的时间。jdk7以后默认使用。
3 jdk8新特性
(1)LongAdder 类似automicLong, 但是提供了“热点分离”。过程如下:如果并发不激烈,则与automicLong 一样,cas赋值。如果出现并发操作,则使用数组,数组的各元素之和为真实value,让操作分散在数组各个元素上,把并发操作压力分散,一遇到并发就扩容数组,最后达到高效率。一般cas如果遇到高并发,可能一直赋值失败导致不断循环,热点分离可以解决这个问题。有点类似concurrenthashmap,分而治之。
(2)completableFuture 对Future进行增强,支持函数式编程的流式调用。提供更多功能,压缩编码量。
(3)stampedLock 改进读写锁,读不阻塞写。如果读的时候,发生了写,应该重新读,不是阻塞写。解决了一般读写锁读太多导致写一直阻塞的问题,读线程发现数据不一致时触发重新读操作。 原理是维护了一个stamp标记,在添加写锁的释放写锁的时候,stamp都会改变(比如++),代码在加读锁的时候,可以先得到stamp,读完数据释放读锁的时候,调用validate方法,检验刚才stamp和现在stamp是否相同,如果相同,说明读的过程中没有修改,读取成功,如果不相同,则说明读的时候发生了写,那么接下来两种策略,一个是继续用当前stamp为初试,继续读,读完比较stamp,是乐观的办法;另一种直接调用readlock(),升级为正常的读锁,是悲观办法。
并发设计模式和锁优化以及jdk8并发新特性的更多相关文章
- Java学习:JDK8的新特性
Java学习:JDK8的新特性 一.十大特性 Lambda表达式 Stream函数式操作流元素集合 接口新增:默认方法与静态方法 方法引用,与Lambda表达式联合使用 引入重复注解 类型注解 最新的 ...
- java 28 - 7 JDK8的新特性 之 接口可以使用方法
JDK8的新特性: http://bbs.itcast.cn/thread-24398-1-1.html 其中之一:接口可以使用方法 interface Inter { //抽象方法 public a ...
- JDK8.0新特性
连接转载地址:http://www.2cto.com/kf/201609/544044.html Eclipse: http://aiyiupload.oss-cn-beijing.aliyuncs. ...
- JDK8的新特性——Lambda表达式
JDK8已经发布快4年的时间了,现在来谈它的新特性显得略微的有点“不合时宜”.尽管JDK8已不再“新”,但它的重要特性之一——Lambda表达式依然是不被大部分开发者所熟练运用,甚至不被开发者所熟知. ...
- JDK8之新特性扩展篇
之前分篇章讲了一些JKD8中添加的新特性,还有一些新特性这里也一并讲下. BASE64 base64编码解码已经被加入到了jdk8中了. import java.nio.charset.Standar ...
- JDk8的新特性-流和内部iteration
JDK8到今天已经出了好几年了 但是在公司能用到新特性的地方还是很少, 去年的时候当时项目老大要求我们用最新的写法来写Java 刚开始看到用stream写出来的代码一脸懵逼,内心就在想 这是Jav ...
- jdk8的新特性 Lambda表达式
很多同学一开始接触Java8可能对Java8 Lambda表达式有点陌生. //这是一个普通的集合 List<Employee> list = em.selectEmployeeByLog ...
- JDK15就要来了,你却还不知道JDK8的新特性!
微信搜「烟雨星空」,白嫖更多好文. 现在 Oracle 官方每隔半年就会出一个 JDK 新版本.按时间来算的话,这个月就要出 JDK15 了.然而,大部分公司还是在使用 JDK7 和 8 . 之前去我 ...
- Oracle12c中性能优化&功能增强新特性之全局索引DROP和TRUNCATE 分区的异步维护
Oracle 12c中,通过延迟相关索引的维护可以优化某些DROP和TRUNCATE分区命令的性能,同时,保持全局索引为有效. 1. 设置 下面的例子演示带全局索引的表创建和加载数据的过程. -- ...
随机推荐
- c的详细学习(6)函数
根据模块化程序设计的原则,一个较大的程序一般要分为若干个小模块,每个模块实现一个比较简单的功能.在c语言中,函数是一个基本的程序模块. (1)函数的基本概念: 1)基本介绍: 任何一个 ...
- python 3 mysql 索引原理与慢查询优化
python 3 mysql 索引原理与慢查询优化 一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最 ...
- 【Flask模板】宏的概念和基本使用
# 宏:模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量.使用宏的时候,参数可以为默认值.相关示例代 ...
- hadoop集群增加新节点
上次hadoop集群一块数据盘报警, 提交工单后维修人员更换硬盘 服务器是dell r720的, 8盘位, 蛋疼的是这些硬盘都是做的单盘raid1,维修人员说必须关机导入硬盘才能正常使用 (服务器就这 ...
- Spring Cloud之网关搭建
统一由网关进行拦截判断 要不放到每个服务里面就很不合适了 冗余 主要的: <dependency> <groupId>org.springframework.cloud< ...
- sql 常用的查询套路
1. 写一个sql:,查询商城每天的用户数及每天累计用户数 date user_count total_count2016-12-01 1 12016- ...
- java:maven中webapp下的jsp不能访问web-inf下面的bean
java:maven中webapp下的jsp不能访问web-inf下面的bean 当然 WEB-INF下面的文件是不能访问的,只能吧jsp文件放入到WEB-INF下面,然后通过配置WEB-INF下we ...
- MVC中ajax调用Controller的方法
1. ajax代码: $.ajax({ async: false, cache: false, type: 'POST', contentType: "application/json&qu ...
- 分享知识-快乐自己:解决 Maven 无法下载 fastdfs-client-java 依赖。
因为fastdfs-client-java-1.27-SNAPSHOT.jar这个依赖包在maven中央仓库是没有的. 需要自己编译源码成jar本地安装到maven 的本地仓库,安装完以后就能正常引用 ...
- C++(三)— 二维容器
1.二维bool向量 vector<vector<bool>> dp(len, vector<bool>(len, false));