一、无锁类的原理

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. 【C/C++】C语言基础知识【第二版】

    基础语法 输出语句 #include <stdio.h> int main(void) { printf("-------分界线1------- \n"); print ...

  2. Python中查看变量的类型,内存地址,所占字节的大小

    查看变量的类型 #利用内置type()函数 >>> nfc=["Packers","49"] >>> afc=[" ...

  3. CSS详细解读定位

    一 前言 CSS定位是CSS布局只能够重要的一环.本篇文章带你解读定位属性,可以让你更加深入的理解定位带来的一些特性,熟练使用CSS布局. 二 正文 1.文档流布局的概念 将窗体自上而下分成一行行, ...

  4. 一步步搭建物联网系统——无处不在的CSS

    无处不在的CSS 或许你觉得CSS一点儿也不重要,而事实上,如果说HTML是建筑的框架,CSS就是房子的装修.那么Javascript呢,我听到的最有趣的说法是小三--还是先让我们回到代码上来吧. C ...

  5. VueJs项目笔记

    ======================知识点总结=========================== 一.keep-alive(实现页面的缓存) 二. 移动端固定定位的解决方案 三. Vue表 ...

  6. js随手笔记-------理解JavaScript碰撞检测算法核心简单实现原理

    碰撞检测在前端游戏,设计拖拽的实用业务等领域的应用场景非常广泛,今天我们就在这里对于前端JavaScript如何实现碰撞检测算法进行一个原理上的探讨,让大家能够明白如何实现碰撞以及碰撞的理念是什么:1 ...

  7. python爬虫---爬取网易云音乐

    代码: import requests from lxml import etree text = requests.get("https://music.163.com/discover/ ...

  8. CentOS系统Tomcat 8.5或9部署SSL证书

    本文档介绍了CentOS系统下Tomcat 8.5或9部署SSL证书的操作说明. 环境准备 操作系统:CentOS 7.6 64位 Web服务器:Tomcat 8.5或9 前提条件 已从阿里云SSL证 ...

  9. MySQL中MyISAM和InnoDB引擎的区别

    区别: 1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事 ...

  10. 控制反转 IOC 理论推导

    控制反转 IOC 理论推导 按照我们传统的开发,我们会先去 dao 层创建一个接口,在接口中定义方法. public interface UserDao { void getUser(); } 然后再 ...