java并发学习--第八章 JDK 8 中线程优化的新特性
一、新增原子类LongAdder
LongAdder是JDK8中AtomicLong的增强工具类,它与AtomicLong最大的不同就是:在多线程场景下,LongAdder中对单一的变量进行拆分成多个变量,这些变量分为两类base和Cell。base是基础值,默认一般为0;而Cell就是我们所拆分的值,它可以有多个。所以当获取LongAdder的值时就是把base和每个Cell的值相加。
为什么要拆分成多个Cell呢?这是因为在多线程场景下,如果多个线程都在对同一个变量进行操作,为了使这个变量原子性,我们不得不对起加锁,这样就大大的降低了程序的性能。但是如果把这个变量拆分为多个Cell,虽然还是会给每个Cell加锁,但是线程访问的变量就是不是同一个了,可以进行异步操作。
关于Cell的特点:
1.Cell采用懒加载机制,这是因为Cell占的内存空间相对比较大的。开始只会创建Base,只有当有其他线程来竞争资源时,才会拆分为多个Cell;
2.Cell的初始化值为2,每次扩容是2的N次方;
3.Cell本质是一个数组,它的元素最大值为 CPU 核数;
4.Cell 扩容条件: casCellsBusy 为 false 没有库容;有线程竞争资源;cell的数量没有超过 CPU的核数。
我们来看一个例子,用LongAdder声明一个值,使这个值加10000*10次,10为线程数:
public class ThreadLongAdder implements Runnable {
//给线程方法传递一个参数
static LongAdder count = new LongAdder();
/**
* 线程任务,将count的值相加10000*10次
*/
public void run() {
System.out.println("当前线程获取count的值为:" + count);
for (int i = 0; i < 10000; i++) {
long num = 1l;
count.add(num);
}
}
public static void main(String[] args) throws InterruptedException {
//创建多线程环境,这里创建了10个线程
Thread[] thread = new Thread[10];
//未创建的多线程中添加线程任务
for (int i = 0; i < 10; i++) {
thread[i] = new Thread(new ThreadLongAdder());
}
//启动每个线程任务
for (int i = 0; i < 10; i++) {
thread[i].start();
}
//join方法的作用是阻塞主线程,防止还没有计算完成,就开始输出count的值了
for (int i = 0; i < 10; i++) {
thread[i].join();
}
System.out.println("count计算的结果是:" + count);
}
}
结果为:

二、增强锁stampedlock
stampedlock是JDK8中新增的加强读写锁。我们知道在高并发场景下,读写锁中读锁与写锁是互斥的,如果当环境中读的操作过多,写的较少,就会导致写操作的线程产生饥饿现象。
对于饥饿现象我们一般会考虑使用公平锁,但是公平锁会大大降低程序的性能。所以为了解决这一问题,JDK8为我们新增了stampedlock来解决。
stampedlock的特点:
- 获取锁的时候,会给获取锁的方法返回一个Stamp,当Stamp的值为0时,表示获取失败。当然释放锁的时候没,释放的方法中必须要有获取锁的时候得到的Stamp。这样做的好处是能够提供读写互斥的性能。
- StampedLock是不可重入锁,如果两个方法获取了同一把锁,那么就会发生死锁;
- StampedLock中为我们提供了3把锁:
①Reading(读锁):类似于ReentrantReadWriteLock的读锁
②Writing(写锁):类似于ReentrantReadWriteLock的写锁
③Optimistic reading(乐观读模式):这是StampedLock为我们提供的一把优化锁 - StampedLock支持读锁和写锁的相互转换
我们知道RRW中,当线程获取到写锁后,可以降级为读锁,但是读锁是不能直接升级为写锁的。
StampedLock提供了读锁和写锁相互转换的功能,使得该类支持更多的应用场景。 - 无论写锁还是读锁,都不支持Conditon等待
我们来看一个stampedlock使用的例子:
线程任务类:
public class StampedlockDemo extends Thread{
StampedLock stampedLock = new StampedLock();
int num;
static int sum;
/***
* 这是线程任务,先写再读
*/
@Override
public void run() {
// 获取写锁,Long类型表示Stamp的值,如果是0就获取失败
Long write = stampedLock.writeLock();
if (write != 0L) {
System.out.println("现在进行写的操作");
// 要写的方法
sum += num;
}
// 转换为读锁
Long read = stampedLock.tryConvertToOptimisticRead(write);
if (read != 0L) {
// 要读的内容
System.out.println("现在进行的读操作,当前的值为:");
System.out.println(sum);
}
}
public StampedlockDemo(StampedLock stampedLock, int num) {
super();
this.stampedLock = stampedLock;
this.num = num;
}
}
测试类:
public class Main {
public static void main(String[] args) {
//创建锁对象
StampedLock stampedLock = new StampedLock();
//创建三个线程
StampedlockDemo stampedlockDemo1=new StampedlockDemo(stampedLock,1);
StampedlockDemo stampedlockDemo2=new StampedlockDemo(stampedLock,10);
StampedlockDemo stampedlockDemo3=new StampedlockDemo(stampedLock,100);
stampedlockDemo3.start();
stampedlockDemo1.start();
stampedlockDemo2.start();
}
}
运行结果:

java并发学习--第八章 JDK 8 中线程优化的新特性的更多相关文章
- Java并发学习之二——获取和设置线程信息
本文是学习网络上的文章时的总结,感谢大家无私的分享. Thread类的对象中保存了一些属性信息可以帮助我们辨别每个线程.知道它的一些信息 ID:每一个线程的独特标示: Name:线程的名称: Prio ...
- java并发学习--第二章 spring boot实现线程的创建
除了之前介绍的创建线程方式外,spring boot为我们了提供一套完整的线程创建方式,其中包括了:线程.线程池.线程的监控. 一.使用spring boot提供的方法创建线程与线程池 1.首先在sp ...
- Oracle12c中性能优化增强新特性之数据库智能闪存
智能闪存功能最初在XD中引入.从Oracle11.2.0.2开始,除了用于XD存储,还可用于任何闪盘.Oracle12c中,不需卷管理器就可以使用闪盘. 1. 简介 智能闪存在solaris和lin ...
- Java并发编程系列-(9) JDK 8/9/10中的并发
9.1 CompletableFuture CompletableFuture是JDK 8中引入的工具类,实现了Future接口,对以往的FutureTask的功能进行了增强. 手动设置完成状态 Co ...
- “全栈2019”Java第八十八章:接口中嵌套接口的应用场景
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第五十八章:多态中方法返回类型可以是子类类型
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Java 9 揭秘(20. JDK 9中API层次的改变)
Tips 做一个终身学习的人. 在最后一章内容中,主要介绍以下内容: 下划线作为新关键字 改进使用try-with-resources块的语法 如何在匿名类中使用<>操作符 如何在接口中使 ...
- 从源码学习Java并发的锁是怎么维护内部线程队列的
从源码学习Java并发的锁是怎么维护内部线程队列的 在上一篇文章中,凯哥对同步组件基础框架- AbstractQueuedSynchronizer(AQS)做了大概的介绍.我们知道AQS能够通过内置的 ...
- Java并发学习(一):进程和线程
好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 前言 俗话说得好"一人 ...
随机推荐
- flask环境布署--废弃不用,只留作备份
[前置条件] 创建1个flask-demo,生成requirement.txt文件(下载好gunicorn),上传至git.创建demo参照:创建一个flask api-demo(响应体显示中文) g ...
- VS2010远程调试C#程序
场景: 客户的计算机运行程序出现异常,由于办公场所不在一起,无法直接在客户的机器上调试.此时希望可以直接在开发人员的计算机(本地机器)上远程调试客户机上的软件. 假设: 本地机器的系统账户为 GIS, ...
- Linux_RHEL7_LDAP、Autofs服务
目录 目录 前言 LDAP 加入LDAP用户认证服务器 文件自动挂载服务autofs 前言 LDAP服务器,用作于网络用户的集中管理.在企业中员工的个人帐号一般采用集中管理的方式,在不同的系统平台上也 ...
- webpack中的 chunk,module,bundle的区别,以及hidden modules是什么
hidden modules是什么: chunk,module,bundle的区别 总结: module是指任意的文件模块,等价于commonjs中的模块 chunks是webpack处理过程中被分组 ...
- MySQL 常用报错注入原理分析
简介 这段时间学习SQL盲注中的报错注入,发现语句就是那么两句,但是一直不知道报错原因,所以看着别人的帖子学习一番,小本本记下来 (1) count() , rand() , group by 1.报 ...
- windows 下使用Linux子系统
在 Windows 上进行 web 开发,比较普遍的方案是使用 phpstudy 或者别的一些集成环境软件进行环境搭建,写好代码后将代码上传至版本管理工具 git/svn,再将代码同步到 Linux ...
- laradock 部署 php 环境 和 laravel/lumen 框架
环境是windows 10 版本1809,docker 版本18.09.0 首先是下载docker.git, 具体可以参考 http://laradock.io/ 要求 Docker >= 17 ...
- Maximum Depth of Binary Tree(二叉树最大深度)
来源:https://leetcode.com/problems/maximum-depth-of-binary-tree Given a binary tree, find its maximum ...
- 关于postman
1 Get 1.1 Params 直接显示在url上,即url参数,用&分隔开. springboot中可以用@RequestParam注解获取. 1.2 Headers 1.3 Body 1 ...
- 剑指Offer编程题(Java实现)——从尾到头打印链表
题目描述 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList. 解题思路 思路一:使用头插法 使用头插法可以得到一个逆序的链表.遍历链表,每次将所遍历节点插入到链表的头部. 头结点和第一个 ...