1.背景

这一节,就是学习常用的cas对象与api

.....

2.原子整数

直接看代码吧,或者看API文档

2.1.AtomicInteger的API演示

package com.ldp.demo06Atomic;

import java.util.concurrent.atomic.AtomicInteger;

/**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/16 9:15
* @description <p>
* 原子整数
* JUC 并发包提供了:
* AtomicBoolean
* AtomicInteger
* AtomicLong
* </p>
*/
public class Test01 {
public static void main(String[] args) {
AtomicInteger i = new AtomicInteger(10);
// 1.
System.out.println("i=" + i.get() + ",执行i.getAndIncrement(),结果为:" + i.getAndIncrement() + ",实际i=" + i.get());
System.out.println("i=" + i.get() + ",执行i.incrementAndGet(),结果为:" + i.incrementAndGet() + ",实际i=" + i.get());
// 2.
System.out.println("i=" + i.get() + ",执行i.getAndDecrement(),结果为:" + i.getAndDecrement() + ",实际i=" + i.get());
System.out.println("i=" + i.get() + ",执行i.decrementAndGet(),结果为:" + i.decrementAndGet() + ",实际i=" + i.get());
// 3.
System.out.println("i=" + i.get() + ",执行i.getAndAdd(10),结果为:" + i.getAndAdd(10) + ",实际i=" + i.get());
System.out.println("i=" + i.get() + ",执行i.addAndGet(10),结果为:" + i.addAndGet(10) + ",实际i=" + i.get());
//4. P表示i的值
System.out.println("i=" + i.get() + ",执行i.getAndUpdate(p -> p - 2),结果为:" + i.getAndUpdate(p -> p - 2) + ",实际i=" + i.get());
System.out.println("i=" + i.get() + ",执行i.updateAndGet(p -> p - 2),结果为:" + i.updateAndGet(p -> p - 2) + ",实际i=" + i.get());
//5. P表示原来的值i , X表示修改的值10
System.out.println("i=" + i.get() + ",执行i.getAndAccumulate(10, (p, x) -> p + x),结果为:" + i.getAndAccumulate(10, (p, x) -> p + x) + ",实际i=" + i.get());
System.out.println("i=" + i.get() + ",执行i.accumulateAndGet(10, (p, x) -> p + x),结果为:" + i.accumulateAndGet(10, (p, x) -> p + x) + ",实际i=" + i.get());
/**
* 测试执行结果
* i=10,执行i.getAndIncrement(),结果为:10,实际i=11
* i=11,执行i.incrementAndGet(),结果为:12,实际i=12
* i=12,执行i.getAndDecrement(),结果为:12,实际i=11
* i=11,执行i.decrementAndGet(),结果为:10,实际i=10
* i=10,执行i.getAndAdd(10),结果为:10,实际i=20
* i=20,执行i.addAndGet(10),结果为:30,实际i=30
* i=30,执行i.getAndUpdate(p -> p - 2),结果为:30,实际i=28
* i=28,执行i.updateAndGet(p -> p - 2),结果为:26,实际i=26
* i=26,执行i.getAndAccumulate(10, (p, x) -> p + x),结果为:26,实际i=36
* i=36,执行i.accumulateAndGet(10, (p, x) -> p + x),结果为:46,实际i=46
*/
}
}

3.原子引用

2.2.AtomicReference的API演示

package com.ldp.demo06Atomic;

import org.junit.Test;

import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicReference; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/16 9:42
* @description <p>
* 原子引用
* 为什么需要原子引用类型?
* AtomicReference
* AtomicMarkableReference
* AtomicStampedReference
* <p>
* Stamp:标记印记
* </p>
*/
public class Test02 {
@Test
public void test01AtomicReference() {
AtomicReference<BigDecimal> ref = new AtomicReference<>(new BigDecimal("3.14"));
while (true) {
BigDecimal prev = ref.get();
BigDecimal next = prev.add(new BigDecimal("0.2"));
boolean b = ref.compareAndSet(prev, next);
if (b) {
System.out.println("修改成功,ref=" + ref.get());
break;
}
}
}
}

2.3.ABA问题与解决

package com.ldp.demo06Atomic;

import com.common.MyThreadUtil;

import java.util.concurrent.atomic.AtomicReference;

/**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/18 8:14
* @description
*/
public class Test03ABA {
private static AtomicReference<String> ref = new AtomicReference<>("A"); /**
* ABA问题与解决
* 代码执行结果:
* A修改为B-->true
* B修改为A-->true
* A修改为C-->true
*
* @param args
*/
public static void main(String[] args) {
String prev = ref.get();
new Thread(() -> {
// A 修改为B
System.out.println("A修改为B-->" + ref.compareAndSet(ref.get(), "B"));
// B修改为A
System.out.println("B修改为A-->" + ref.compareAndSet(ref.get(), "A"));
}, "ABA").start();
MyThreadUtil.sleep(1);
// A修改为C
System.out.println("A修改为C-->" + ref.compareAndSet(prev, "C"));
}
}

2.4.AtomicStampedReference解决ABA问题

package com.ldp.demo06Atomic;

import com.common.MyThreadUtil;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/18 8:14
* @description
*/
public class Test03ABAStamped {
private static AtomicStampedReference<String> ref = new AtomicStampedReference<String>("A", 1); /**
* ABA问题解决思路
* <p>
* 主线程仅能判断出共享变量的值与最初值 A 是否相同,不知到从 A 改为 B 又 改回 A 的情况
* 如果主线程希望:只要有其它线程【修改了】共享变量,那么自己的 cas 就算失败,
* 这时,需要比较值和版本号
* <p>
* 代码执行结果:
* A修改为B-->true
* B修改为A-->true
* A修改为C-->false
*
* @param args
*/
public static void main(String[] args) {
String prev = ref.getReference();
int stamp = ref.getStamp();
new Thread(() -> {
// A 修改为B
System.out.println("A修改为B-->" + ref.compareAndSet(ref.getReference(), "B", ref.getStamp(), ref.getStamp() + 1));
// B修改为A
System.out.println("B修改为A-->" + ref.compareAndSet(ref.getReference(), "A", ref.getStamp(), ref.getStamp() + 1));
}, "ABA").start();
MyThreadUtil.sleep(1);
// A修改为C
System.out.println("A修改为C-->" + ref.compareAndSet(prev, "C", stamp, stamp + 1));
}
}

2.5.AtomicMarkableReference解决ABA问题

package com.ldp.demo06Atomic;

import com.common.MyThreadUtil;

import java.util.concurrent.atomic.AtomicMarkableReference;

/**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/18 8:14
* @description
*/
public class Test03Markable {
private static AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("A", true); /**
* ABA问题与解决
* 代码执行结果:
* marked-1=true
* A修改为B-->true
* marked-2=false
* B修改为A-->true
* marked-3=true
* A修改为C-->true
* marked-4=false
* 有时候,并不关心引用变量更改了几次,只是关心是否更改过,所以就有了AtomicMarkableReference
*
* @param args
*/
public static void main(String[] args) {
String prev = ref.getReference();
System.out.println("marked-1=" + ref.isMarked());
new Thread(() -> {
// A 修改为B
System.out.println("A修改为B-->" + ref.compareAndSet(ref.getReference(), "B", true, false));
System.out.println("marked-2=" + ref.isMarked());
// B修改为A
System.out.println("B修改为A-->" + ref.compareAndSet(ref.getReference(), "A", false, true));
}, "ABA").start();
MyThreadUtil.sleep(1);
System.out.println("marked-3=" + ref.isMarked());
// A修改为C
System.out.println("A修改为C-->" + ref.compareAndSet(prev, "C", true, false));
System.out.println("marked-4=" + ref.isMarked());
}
}

4.原子数组

package com.ldp.demo06Atomic;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/18 8:55
* @description <p>
* 原子数组
* AtomicIntegerArray
* AtomicLongArray
* AtomicReferenceArray
* </p>
*/
public class Test04Array {
/**
* 测试自定义的数组
* int[10] 存在线程不安全
*/
@Test
public void test01() throws InterruptedException {
myArray(() -> new int[10],
(array) -> array.length,
(array, index) -> array[index]++,
(array) -> System.out.println(Arrays.toString(array)));
} /**
* 测试自定义的数组
* AtomicIntegerArray 线程安全
*/
@Test
public void test02() throws InterruptedException {
myArray(
() -> new AtomicIntegerArray(10),
(array) -> array.length(),
(array, index) -> array.getAndIncrement(index),
array -> System.out.println(array)
);
} /**
* @param supplierArray 提供一个数组
* @param functionLength 获取数组长度
* @param addArray 数组元素自增1
* @param printArray 打印数组
* @param <T> 数组的泛型
*/
public <T> void myArray(Supplier<T> supplierArray,
Function<T, Integer> functionLength,
BiConsumer<T, Integer> addArray,
Consumer<T> printArray
) throws InterruptedException {
// 定义一个放线程的集合
List<Thread> list = new ArrayList<>();
// 1.提供一个数组
T array = supplierArray.get();
// 2.获取数组长度的方法
Integer length = functionLength.apply(array);
// 遍历数组,使用1000个线程,每个线程给每个元素加1
for (int i = 0; i < length; i++) {
// 1000个线程,每个线程给每个元素加1
Thread t = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
// 3.自增方法,传入array与下标index
addArray.accept(array, j % length);
}
});
list.add(t);
}
// 启动所有线程
for (Thread thread : list) {
thread.start();
}
// 等待所有线程执行完
for (Thread thread : list) {
thread.join();
}
// 4.打印数组的方法
printArray.accept(array);
}
}

5.字段更新器

package com.ldp.demo06Atomic;

import org.junit.Test;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/18 9:56
* @description <p>
* 字段更新器
* AtomicReferenceFieldUpdater
* AtomicIntegerFieldUpdater
* AtomicLongFieldUpdater
* </p>
*/
public class Test05Field {
private volatile int age = 18; @Test
public void test01() {
AtomicIntegerFieldUpdater fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Test05Field.class, "age");
// 创建Test05Field对象
Test05Field test05Field = new Test05Field();
// 将18修改为 30岁
fieldUpdater.compareAndSet(test05Field, test05Field.age, 30);
System.out.println("当前年龄:" + test05Field.age);
}
}

6.原子累加器:LongAdder

package com.ldp.demo06Atomic;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.function.Supplier; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/18 10:13
* @description
*/
@Slf4j
public class Test06LongAdder {
/**
* AtomicLong 与 LongAdder 测试
* LongAdder:在多线程并发的情况下,LongAdder会明显优于AtomicLong
* <p>
* LongAdder的原理:
* 在由线程竞争cpu时,设置多个累加单元,Therad-0 累加 Cell[0],而 Thread-1 累加Cell[1]...
* 最后将结果汇总。这样它们在累加时操作的不同的 Cell 变量,因此减少了 CAS 重试失败,从而提高性能。
* <p>
* 09:35:18.967 [main] -> start.....
* num=10000000,耗时:172 毫秒
* num=10000000,耗时:203 毫秒
* num=10000000,耗时:187 毫秒
* num=10000000,耗时:188 毫秒
* num=10000000,耗时:171 毫秒
* 09:35:20.013 [main] -> ------------------------------
* num=10000000,耗时:157 毫秒
* num=10000000,耗时:93 毫秒
* num=10000000,耗时:63 毫秒
* num=10000000,耗时:78 毫秒
* num=10000000,耗时:78 毫秒
* 09:35:20.482 [main] -> end.....
*
* @throws InterruptedException
*/
@Test
public void test01() throws InterruptedException {
log.info("start.....");
for (int i = 0; i < 5; i++) {
methodAdd(() -> new AtomicLong(),
(num) -> num.getAndIncrement());
}
log.info("------------------------------");
for (int i = 0; i < 5; i++) {
methodAdd(() -> new LongAdder(),
(num) -> num.increment());
}
log.info("end.....");
} /**
* 给一个数组累加
*
* @param supplierNum 传入的数字
* @param addNum 数字加一
* @param <T>
*/
public <T> void methodAdd(Supplier<T> supplierNum, Consumer<T> addNum) throws InterruptedException {
long start = System.currentTimeMillis();
List<Thread> list = new ArrayList<>();
T num = supplierNum.get();
// 3个线程
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(() -> {
// 每个线程加 10万次
for (int j = 0; j < 100000; j++) {
addNum.accept(num);
}
});
list.add(thread);
}
// 启动线程
for (Thread thread : list) {
thread.start();
}
// 等待线程结束
for (Thread thread : list) {
thread.join();
}
long end = System.currentTimeMillis();
System.out.println("num=" + num + ",耗时:" + ((end - start)) + " 毫秒");
}
}

7.CAS实现自定义锁

package com.ldp.demo06Atomic;

import java.util.concurrent.atomic.AtomicInteger;

/**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/19 9:42
* @description <p>
* 利用cas的特性实现自己的锁
* </p>
*/
public class Test07MyCasLock {
AtomicInteger lock = new AtomicInteger(0); /**
* 获取锁
*/
public void lock() {
while (true) {
if (lock.compareAndSet(0, 1)) {
// 取锁成功
break;
}
}
} /**
* 释放锁
*/
public void unlock() {
lock.set(0);
} /**
* 测试自定义的锁
* 注意:自定义的锁只是练习对cas的理解,不作为生产的实现方式
*
* @param args
*/
public static void main(String[] args) {
// UnsafeAccessor.
Test07MyCasLock casLock = new Test07MyCasLock();
casLock.lock();
try {
System.out.println("加锁成功....");
} finally {
casLock.unlock();
System.out.println("锁已经释放...");
}
}
}

完美!

java多线程之-CAS无锁-常见API的更多相关文章

  1. java 多线程12 : 无锁 实现CAS原子性操作----原子类

    由于java 多线程11:volatile关键字该文讲道可以使用不带锁的情况也就是无锁使变量变成可见,这里就理解下如何在无锁的情况对线程变量进行CAS原子性及可见性操作 我们知道,在并发的环境下,要实 ...

  2. java并发:AtomicInteger 以及CAS无锁算法【转载】

    1 AtomicInteger解析 众所周知,在多线程并发的情况下,对于成员变量,可能是线程不安全的: 一个很简单的例子,假设我存在两个线程,让一个整数自增1000次,那么最终的值应该是1000:但是 ...

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

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

  4. CAS无锁算法与ConcurrentLinkedQueue

    CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...

  5. Java多线程专题5: JUC, 锁

    合集目录 Java多线程专题5: JUC, 锁 什么是可重入锁.公平锁.非公平锁.独占锁.共享锁 可重入锁 ReentrantLock A ReentrantLock is owned by the ...

  6. CAS无锁机制原理

    原子类 java.util.concurrent.atomic包:原子类的小工具包,支持在单个变量上解除锁的线程安全编程 原子变量类相当于一种泛化的 volatile 变量,能够支持原子的和有条件的读 ...

  7. Java多线程6:Synchronized锁代码块(this和任意对象)

    一.Synchronized(this)锁代码块 用关键字synchronized修饰方法在有些情况下是有弊端的,若是执行该方法所需的时间比较长,线程1执行该方法的时候,线程2就必须等待.这种情况下就 ...

  8. Java多线程5:Synchronized锁机制

    一.前言 在多线程中,有时会出现多个线程对同一个对象的变量进行并发访问的情形,如果不做正确的同步处理,那么产生的后果就是“脏读”,也就是获取到的数据其实是被修改过的. 二.引入Synchronized ...

  9. java多线程--6 死锁问题 锁Lock

    java多线程--6 死锁问题 锁Lock 死锁问题 多个线程互相抱着对方需要的资源,然后形成僵持 死锁状态 package com.ssl.demo05; public class DeadLock ...

  10. java 多线程总结篇4——锁机制

    在开发Java多线程应用程序中,各个线程之间由于要共享资源,必须用到锁机制.Java提供了多种多线程锁机制的实现方式,常见的有synchronized.ReentrantLock.Semaphore. ...

随机推荐

  1. 在线Base64转文件、文件转Base64工具

    在线Base64转换神器,一键实现Base64编码与文件互转!支持图片.文档等各类文件,快速准确,安全无服务器存储.拖拽上传,轻松编码解码,提升开发效率.跨平台兼容,移动端友好,让数据转换再无障碍. ...

  2. 一文学完所有的Hive Sql(两万字最全详解)

    Hive Sql 大全 本文基本涵盖了Hive日常使用的所有SQL,因为SQL太多,所以将SQL进行了如下分类: 一.DDL语句(数据定义语句): 对数据库的操作:包含创建.修改数据库 对数据表的操作 ...

  3. 十大java应用服务器(web server)总结

    java应用服务器(web server),是指运行java程序的web应用服务器软件,不包括nginx.Apache等通用web服务器软件. 一.Tomcat Tomcat是Apache 软件基金会 ...

  4. 配置pod拉取harbor容器镜像仓库私有镜像:secret保存账号密码

    目录 一.系统环境 二.前言 三.Docker-Registry类型的Secret简介 四.镜像仓库简介 五.搭建Harbor容器镜像仓库 5.1 安装Harbor 5.2 创建项目 5.3 推送镜像 ...

  5. hive第二课:Hive3.1.2分区与排序以及分桶(内置函数)

    Hive3.1.2分区与排序(内置函数) 1.Hive分区(十分重要!!) 分区的目的:避免全表扫描,加快查询速度! 在大数据中,最常见的一种思想就是分治,我们可以把大的文件切割划分成一个个的小的文件 ...

  6. shell 根据 指定列 进行 去除 重复行

    根据指定列进行去除重复行 这里的重复是指如果两行的某一列数据相同,则认为是重复数据. 例如:第1行与第2行数据,其中的第2列(以- 作为分隔符)明显是重复的. 100069 - ARM Compile ...

  7. .net core 3.1 + 动态执行C#

    1.使用 using Microsoft.CodeAnalysis.CSharp.Scripting;using Microsoft.CodeAnalysis.Scripting; 2.定义 Rosl ...

  8. 【进阶篇】Java 项目中对使用递归的理解分享

    [进阶篇]Java 项目中对使用递归的理解分享 目录 [进阶篇]Java 项目中对使用递归的理解分享 前言 一.什么是递归 1.1基本概念 1.2优缺点 1.3与迭代的区别 二.实际案例 三.改进方案 ...

  9. vs code 设置中文

    1.安装 下载地址:官网   打开 安装后打开默认显示英文界面. 2.修改 使用快捷键 ctrl+shift+p, 输入configure display language 下拉框选择 install ...

  10. 全网最适合入门的面向对象编程教程:12 类和对象的Python实现-Python使用logging模块输出程序运行日志

    全网最适合入门的面向对象编程教程:12 类和对象的 Python 实现-Python 使用 logging 模块输出程序运行日志 摘要: 本文主要介绍了日志的定义和作用,以及 Python 内置日志处 ...