一、无锁类的原理

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实现

Java高并发-无锁的更多相关文章

  1. 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  2. (转载)java高并发:CAS无锁原理及广泛应用

    java高并发:CAS无锁原理及广泛应用   版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...

  3. Java高并发程序设计学习笔记(四):无锁

    转自:https://blog.csdn.net/dataiyangu/article/details/86440836#1__3 1. 无锁类的原理详解简介:1.1. CAS1.2. CPU指令2. ...

  4. 【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray

    除了提供基本数据类型外,JDK还为我们准备了数组等复合结构.当前可用的原子数组有:AtomicIntegerArray.AtomicLongArray和AtomicReferenceArray,分别表 ...

  5. java高并发系列 - 第12天JUC:ReentrantLock重入锁

    java高并发系列 - 第12天JUC:ReentrantLock重入锁 本篇文章开始将juc中常用的一些类,估计会有十来篇. synchronized的局限性 synchronized是java内置 ...

  6. Java并发 - (无锁)篇6

    , 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 本文主要介绍了死锁的概念与一些相关的基础类, 摘录自葛一鸣与郭超的 [Java高并发程序设计]. 无锁是一种乐观的策略, 它假设对资源的访问是没 ...

  7. Java高并发与多线程(四)-----锁

    今天,我们开始Java高并发与多线程的第四篇,锁. 之前的三篇,基本上都是在讲一些概念性和基础性的东西,东西有点零碎,但是像文科科目一样,记住就好了. 但是本篇是高并发里面真正的基石,需要大量的理解和 ...

  8. java高并发锁的3种实现

    初级技巧 - 乐观锁 乐观锁适合这样的场景:读不会冲突,写会冲突.同时读的频率远大于写. 以下面的代码为例,悲观锁的实现: Java代码   public Object get(Object key) ...

  9. 构建高性能服务(二)java高并发锁的3种实现

    构建高性能服务(二)java高并发锁的3种实现 来源:http://www.xymyeah.com/?p=46   提高系统并发吞吐能力是构建高性能服务的重点和难点.通常review代码时看到sync ...

随机推荐

  1. STM32 之 HAL库(固件库) _

    1 STM32的三种开发方式 通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的.一般大多数都会选用标准库和HAL库,而极少部分人会通过直 ...

  2. CAN总线系列讲座第五讲——CAN总线硬件电路设计

    CAN总线系列讲座第五讲--CAN总线硬件电路设计一  实战学习背景 CAN总线节点的硬件构成方案有两种,其中的方案:(1).MCU控制器+独立CAN控制器+CAN收发器.独立CAN控制器如SJA10 ...

  3. GoLang数组切片

    1. 数组1.1 如何定义数组同java数组一样,数组是一组内存连续且类型相同的数据组成 //不初始化初始值默认为0 var arr1 = [5]int{} var arr2 = [5]int{1,2 ...

  4. SQL Server中如何让SQL语句对字符串大小写敏感

    在SQL Server中默认对大小写是不敏感的,例如fname='peter'和fname='PETER'结果是一样的.但有时候用户会要求区分大小写,如验证密码等.这种情况下的处理办法就是在字段后加上 ...

  5. 前端网络安全——Cookies

    一.Cookies特性 1.前端数据存储 2.后端通过http头设置 3.请求时通过http头传给后端 4.前端可读写 5.遵守同源策略 二.Cookies内容 1.域名 2.有效期,删除cookie ...

  6. c++语法拾遗,一些细节与特性

    写了2年多的C+STL的acmer,在学习<C++ primer>时总结的一些少见的语法特性与细节.总体还是和题目说的一样这是一篇 c++ 拾遗. 1 变量和基本类型 1.1 基本类型 1 ...

  7. IDEA修改代码后不用重新启动项目即可刷新

    1.File--Settings--Build 2.Build,Execution,Deplyment--Compiler 3.选中打勾 "Build project automatical ...

  8. git生成和添加SSH公钥

    一 .前言: 大家换电脑.换公司的时候,经常要关联本地git和git线上仓库, 在这里我就顺便记一下,好记性不如烂笔头, 以后找起来来方便 二 .查看自己是否生成过公钥,有的话可以直接拿过来用, 也可 ...

  9. vue简单的父子组件之间传值

     todo-list为例子: 代码: 父传子--------------属性  v-bind 子传父--------------$emit <!DOCTYPE html> <html ...

  10. [转]Fabric2.3中使用test-network搭建测试网络

    这个测试网络一方面可以用来学习Fabric,另一方面也可以让一些更有经验的开发者来测试他们的智能合约和应用,但是不建议用于生产环境,在2.0版本后,这个测试网络也取代了原来的"first-n ...