java8线程特性_java并发学习--第八章 JDK 8 中线程优化的新特性

本文介绍了Java 8中两种线程优化的新特性:LongAdder和StampedLock。LongAdder是AtomicLong的增强版,通过拆分变量减少锁竞争,提高并发性能。StampedLock是一种更灵活的读写锁,支持读写转换,避免饥饿现象,同时提供了乐观读模式。文中通过实例展示了它们的使用方法和优势。
摘要由CSDN通过智能技术生成

一、新增原子类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 implementsRunnable {//给线程方法传递一个参数

static LongAdder count = newLongAdder();/*** 线程任务,将count的值相加10000*10次*/

public voidrun() {

System.out.println("当前线程获取count的值为:" +count);for (int i = 0; i < 10000; i++) {long num = 1l;

count.add(num);

}

}public static void main(String[] args) throwsInterruptedException {//创建多线程环境,这里创建了10个线程

Thread[] thread = new Thread[10];//未创建的多线程中添加线程任务

for (int i = 0; i < 10; i++) {

thread[i]= new Thread(newThreadLongAdder());

}//启动每个线程任务

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);

}

}

结果为:

0eaa6b391f5e7c3af6ae55dcf25fd2f3.png

二、增强锁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 extendsThread{

StampedLock stampedLock= newStampedLock();intnum;static intsum;/***

* 这是线程任务,先写再读*/@Overridepublic voidrun() {//获取写锁,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, intnum) {super();this.stampedLock =stampedLock;this.num =num;

}

}

测试类:

public classMain {public static voidmain(String[] args) {//创建锁对象

StampedLock stampedLock = newStampedLock();//创建三个线程

StampedlockDemo stampedlockDemo1=new StampedlockDemo(stampedLock,1);

StampedlockDemo stampedlockDemo2=new StampedlockDemo(stampedLock,10);

StampedlockDemo stampedlockDemo3=new StampedlockDemo(stampedLock,100);

stampedlockDemo3.start();

stampedlockDemo1.start();

stampedlockDemo2.start();

}

}

运行结果:

435378abfbf2d41c8527e54fce69b0ff.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值