-
Java高并发-无锁
一、无锁类的原理
1.1 CAS
CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值 。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
1.2 CPU指令
二、无锁类的使用
2.1 AtomicInteger
概述
java.util.concurrent.atomic
public class AtomicInteger extends Number implements java.io.Serializable
主要接口
public final int get() // 获取当前值
public final void set(int newValue) // 设置当前值
public final int getAndSet(int newValue) // 设置新值,并返回旧值
public final boolean compareAndSet(int expect, int u) // 如果当前值为expect,则设置为u
public final int getAndIncrement() // 当前值加1,返回旧值,类似于i++
public final int getAndDecrement() // 当前值减1,返回旧值,类似于i--
public final int getAndAdd(int delta) // 当前值增加delta,返回旧值
public final int incrementAndGet() // 当前值加1,返回新值,类似于++i
public final int decrementAndGet() // 当前值减1,返回新值,类似于--i
public final int addAndGet(int delta) // 当前值增加delta,返回新值
主要接口的实现
// 内部定义了一个value,AtomicInteger只是对它的封装
private volatile int value;
compareAndSet
valueOffset是偏移量
举例
2.2 Unsafe
概述
非安全的操作,比如:根据偏移量设置值
park() 把线程停下来
底层的CAS操作
非公开API,在不同版本的JDK中,可能有较大差异
主要接口
// 获得给定对象偏移量上的int值
public native int getInt(java.lang.Object arg0, long arg1);
// 设置给定对象像偏移量上的int值
public native void putInt(java.lang.Object arg0, long arg1, int arg2);
// 获得字段在对象中的偏移量
public native long objectFieldOffset(Field f);
// 设置给定对象的int值,使用volatile语义
public native void putIntVolatile(Object o, long offset, int x);
// 获得给定对象的int值,使用volatile语义
public native void getIntVolatile(Object o, long offset);
// 和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的
public native void putOrderedInt(Object o, long offset, int x);
2.3 AtomicReference
概述
对引用进行修改
是一个模板类,抽象化了数据类型
主要接口
get()
set(V)
compareAndSet()
getAndSet(V)
举例
2.4 AtomicStampedReference
概述
解决ABA问题
一个变量初始值为A
线程1读取变量 00:00
线程2读取变量 00:03
线程2将变量修改为B 00:05
线程3读取变量 00:06
线程3将变量修改为A 00:08
线程1根据变量做计算 00:10
线程1将变量修改为C 00:12 定稿前变量值是A,其实该变量已经经历了ABA的变化
主要接口
// 比较设置 参数依次为:期望值 写入新值 期望时间戳 新时间戳
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp)
// 获得当前对象引用
public V getReference()
// 获得当前时间戳
public int getStamp()
// 设置当前对象引用和时间戳
public void set(V newReference, int newStamp)
举例
public class AtomicStampedReferenceDemo {
static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0);
public static void main(String[] args) {
// 模拟3个充钱线程,余额小余20时充钱且只充一次
for (int i = 0; i < 3; i++) {
final int timestamp = money.getStamp();
new Thread() {
public void run() {
while (true) {
Integer m = money.getReference();
if (m < 20) {
if (money.compareAndSet(m, m + 20, timestamp, timestamp + 1)) {
System.out.println("余额小于20元,充值成功,余额:" + money.getReference());
}
} else {
// 余额大于20,无需充值
break;
}
}
}
}.start();
}
// 模拟一个消费线程
new Thread() {
public void run() {
// 消费10次,余额大于10元时才能消费
for (int i = 0; i < 10; i++) {
while (true) {
int timestamp = money.getStamp();
Integer m = money.getReference();
if (m > 10) {
if (money.compareAndSet(m, m - 10, timestamp, timestamp + 1)) {
System.out.println("成功消费10元,余额:" + money.getReference());
break;
}
} else {
System.out.println("没有足够的余额");
break;
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}.start();
}
}
2.5 AtomicIntegerArray
概述
支持无锁的数组
主要接口
// 获得数组第i个下标的元素
public final int get(int i)
// 获得数组的长度
public final int length()
// 将数组第i个下标设置为newValue,并返回旧的值
public final int getAndSet(int i, int newValue)
// 进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true
public final boolean compareAndSet(int i, int expect, int update)
// 将第i个下标的元素加1
public final int getAndIncrement(int i)
// 将第i个下标的元素减1
public final int getAndDecrement(int i)
// 将第i个下标的元素增加delta(delta可以是负数)
public final int getAndAdd(int i, int delta)
举例
2.6 AtomicIntegerFieldUpdater
概述
让普通变量也享受原子操作
主要接口
// 工厂方法
AtomicIntegerFieldUpdater.newUpdater()
incrementAndGet()
说明
1. Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量。如果变量不可见,就会出错。比如如果score声明为private,就是不可行的。
2. 为了确保变量被正确读取,它必须是volatile类型的。如果我们原有代码中未声明这个类型,那么简单声明一个就行,这不会引起什么问题。
3. 由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此它不支持static字段(Unsafe.objectFieldOffset()不支持静态变量)
举例
三、无锁算法
3.1 Vector实现
add方法
说明
数组实现
modCount++ 记录被修改的次数
ensureCapacityHelper(elementCount+1) 做容量检查
扩容
3.2 无锁的Vector实现
出处:https://www.cnblogs.com/okokabcd/p/8730427.html
栏目列表
最新更新
80386学习(二) 80386特权级保护
80386学习(一) 80386CPU介绍
8086汇编语言学习(十) 8086中断
8086汇编语言学习(九) 8086标志寄存器
8086汇编语言学习(九) 8086标志寄存器
8086汇编语言学习(八) 8086子程序
8086汇编语言学习(六) 8086处理结构化数据
8086汇编语言学习(五) 8086寻址方式
8086汇编语言学习(四) 8086汇编程序的编译
8086汇编语言学习(三) 8086中的段和栈
三大常用数据库事务详解之三:事务运行
三大常用关系型数据库事务详解之二:基
三大关系型数据库事务详解之一:基本概
MongoDB常用命令(2)
MongoDB基本介绍与安装(1)
SQLServer触发器调用JavaWeb接口
SQL Server索引的原理深入解析
SqlServer2016模糊匹配的三种方式及效率问题
SQL中Truncate的用法
sqlserver 多表关联时在where语句中慎用tri
在vscode中使用R时,用快捷键来快捷键入卡
VB.NET中如何快速访问注册表
ASP.NET中图象处理过程详解
Vue(1)Vue安装与使用
JavaScript 语言入门
js将一段字符串的首字母转成大写
纯原生html编写的h5视频播放器
H5仿原生app短信验证码vue2.0组件附源码地
TypeScript(4)接口
TypeScript(3)基础类型