在 java5 以后,我们接触到了线程原子性操作,也就是在修改时我们只需要保证它的那个瞬间是安全的即可,经过相应的包装后可以再处理对象的并发修改,本文总结一下Atomic系列的类的使用方法,其中包含:

类型 Integer Long
基本类型 AtomicInteger AtomicLong AtomicBoolean
数组类型 AtomicIntegerArray AtomicLongArray AtomicReferenceArray
属性原子修改器 AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater

1. 基本类型的使用

首先看一下AtomicInteger的使用,AtomicInteger主要是针对整数的修改的,看一下示例代码:

public class AtomicIntegerDemo {

	/**
* 常见的方法列表
* @see AtomicInteger#get() 直接返回值
* @see AtomicInteger#getAndAdd(int) 增加指定的数据,返回变化前的数据
* @see AtomicInteger#getAndDecrement() 减少1,返回减少前的数据
* @see AtomicInteger#getAndIncrement() 增加1,返回增加前的数据
* @see AtomicInteger#getAndSet(int) 设置指定的数据,返回设置前的数据
*
* @see AtomicInteger#addAndGet(int) 增加指定的数据后返回增加后的数据
* @see AtomicInteger#decrementAndGet() 减少1,返回减少后的值
* @see AtomicInteger#incrementAndGet() 增加1,返回增加后的值
* @see AtomicInteger#lazySet(int) 仅仅当get时才会set
*
* @see AtomicInteger#compareAndSet(int, int) 尝试新增后对比,若增加成功则返回true否则返回false
*/
public final static AtomicInteger TEST_INTEGER = new AtomicInteger(1); public static void main(String []args) { for(int i = 0 ; i < 10 ; i++) { //开启10个线程 new Thread() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int now = TEST_INTEGER.incrementAndGet(); //自增
System.out.println(Thread.currentThread().getName() + " get value : " + now);
}
}.start();
}
}
}

看一下结果:

Thread-3 get value : 4

Thread-7 get value : 5

Thread-9 get value : 9

Thread-4 get value : 6

Thread-0 get value : 3

Thread-1 get value : 8

Thread-5 get value : 11

Thread-8 get value : 7

Thread-2 get value : 10

Thread-6 get value : 2

可以看出,10个线程之间是线程安全的,并没有冲突。也就是说,我们使用原子性操作类去操作基本类型int就可以解决线程安全问题,一个线程在操作的时候,会对其它线程进行排斥,不用我们手动去使用synchronized实现互斥操作了。AtomicLong和AtomicBoolean类似,就不举例子了。

2. 数组类型的使用

下面要开始说Atomic的数组用法,Atomic的数组要求不允许修改长度等,不像集合类那么丰富的操作,不过它可以让数组上每个元素的操作绝对安全的,也就是它细化的力度还是到数组上的元素,做了二次包装,虽然是数组类型的,但是最后还是操作数组中存的数,所以会了上面的基本类型的话,数组类型也很好理解。这里主要看一下AtomicIntegerArray的使用,其它的类似。

public class AtomicIntegerArrayTest {

	/**
* 常见的方法列表
* @see AtomicIntegerArray#addAndGet(int, int) 执行加法,第一个参数为数组的下标,第二个参数为增加的数量,返回增加后的结果
* @see AtomicIntegerArray#compareAndSet(int, int, int) 对比修改,参数1:数组下标,参数2:原始值,参数3,修改目标值,修改成功返回true否则false
* @see AtomicIntegerArray#decrementAndGet(int) 参数为数组下标,将数组对应数字减少1,返回减少后的数据
* @see AtomicIntegerArray#incrementAndGet(int) 参数为数组下标,将数组对应数字增加1,返回增加后的数据
*
* @see AtomicIntegerArray#getAndAdd(int, int) 和addAndGet类似,区别是返回值是变化前的数据
* @see AtomicIntegerArray#getAndDecrement(int) 和decrementAndGet类似,区别是返回变化前的数据
* @see AtomicIntegerArray#getAndIncrement(int) 和incrementAndGet类似,区别是返回变化前的数据
* @see AtomicIntegerArray#getAndSet(int, int) 将对应下标的数字设置为指定值,第二个参数为设置的值,返回是变化前的数据
*/
private final static AtomicIntegerArray ATOMIC_INTEGER_ARRAY = new AtomicIntegerArray(new int[]{1,2,3,4,5,6,7,8,9,10}); public static void main(String []args) throws InterruptedException {
Thread []threads = new Thread[10];
for(int i = 0 ; i < 10 ; i++) {
final int index = i;
final int threadNum = i;
threads[i] = new Thread() {
public void run() {
int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + 1);
System.out.println("线程编号为:" + threadNum + " , 对应的原始值为:" + (index + 1) + ",增加后的结果为:" + result);
}
};
threads[i].start();
}
for(Thread thread : threads) {
thread.join();
}
System.out.println("=========================>\n执行已经完成,结果列表:");
for(int i = 0 ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
}
}
}

运行结果是给每个数组元素加上相同的值,它们之间互不影响。

3. 作为类属性的使用

当某个数据类型是某个类中的一个属性的时候,然后我们要操作该数据,就需要使用属性原子修改器了,这里还是以Integer为例,即:AtomicIntegerFieldUpdater。示例代码如下:

public class AtomicIntegerFieldUpdaterTest {  

    static class A {
volatile int intValue = 100;
} /**
* 可以直接访问对应的变量,进行修改和处理
* 条件:要在可访问的区域内,如果是private或挎包访问default类型以及非父亲类的protected均无法访问到
* 其次访问对象不能是static类型的变量(因为在计算属性的偏移量的时候无法计算),也不能是final类型的变量(因为根本无法修改),必须是普通的成员变量
*
* 方法(说明上和AtomicInteger几乎一致,唯一的区别是第一个参数需要传入对象的引用)
* @see AtomicIntegerFieldUpdater#addAndGet(Object, int)
* @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int)
* @see AtomicIntegerFieldUpdater#decrementAndGet(Object)
* @see AtomicIntegerFieldUpdater#incrementAndGet(Object)
*
* @see AtomicIntegerFieldUpdater#getAndAdd(Object, int)
* @see AtomicIntegerFieldUpdater#getAndDecrement(Object)
* @see AtomicIntegerFieldUpdater#getAndIncrement(Object)
* @see AtomicIntegerFieldUpdater#getAndSet(Object, int)
*/
public final static AtomicIntegerFieldUpdater<A> ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue"); public static void main(String []args) {
final A a = new A();
for(int i = 0 ; i < 10 ; i++) { new Thread() {
public void run() {
if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, 100, 120)) {
System.out.println(Thread.currentThread().getName() + " 对对应的值做了修改!");
}
}
}.start();
}
}
}

可以看到,这里需要将类和类属性传进去才行,传进去后其实跟前面操作Integer没什么不同了,本质都一样的,运行一下,结果只有一个线程能对其进行修改。

线程的原子性操作类的使用就简单总结到这,其他的操作类原理都相似,可以参考 JDK 的文档,可以很容易写出相应的测试代码。

原子性操作类的使用就分享这么多,如有错误之处,欢迎指正,我们一同进步~

Java并发基础10:原子性操作类的使用的更多相关文章

  1. 02.java并发编程之原子性操作

    一.原子性操作 1.ThreadLocal 不同线程操作同一个 ThreadLocal 对象执行各种操作而不会影响其他线程里的值 注意:虽然ThreadLocal很有用,但是它作为一种线程级别的全局变 ...

  2. JAVA多线程提高五:原子性操作类的应用

    当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可能i不等于3,而是等于2.因为A和B线程在更新变量i ...

  3. JAVA多线程学习九-原子性操作类的应用

    当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可能i不等于3,而是等于2.因为A和B线程在更新变量i ...

  4. 5、探秘JDK5新并发库之原子性操作类

    java.util.concurrent.atomic包里提供了 AtomicBoolean 可以用原子方式更新的 boolean 值. AtomicInteger 可以用原子方式更新的 int 值. ...

  5. 并发库应用之二 & Java原子性操作类应用

    Java5的线程并发库中,提供了一组atomic class来帮助我们简化同步处理.基本工作原理是使用了同步synchronized的方法实现了对一个long, integer, 对象的增.减.赋值( ...

  6. Java 并发基础

    Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...

  7. 【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!

    本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ...

  8. java并发基础(五)--- 线程池的使用

    第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...

  9. java并发基础(二)

    <java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ...

随机推荐

  1. 关于使用fastjson出现的问题:com.alibaba.fastjson.JSONException: syntax error, expect {, actual string, pos 1, fastjson-version 1.2.44

    先说下需求:是从redis中根据keys批量获取数据集合,再通过fastjson转为对象集合 代码如下: 在postman测试后,出现错误如下: 刚开始以为是使用fstjson方法不对,后面先通过打断 ...

  2. 必备技能二、es6

    一.ES6模块 ES6 引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量. ES6 的模块化分为导出(export) @与导入(import)两个模块. 特点 ES6 的 ...

  3. django中CBV源码分析

    前言:Django的视图处理方式有两种: FBV(function base views) 是在视图里基于函数形式处理请求. CBV(class base views)是在视图里基于类的形式处理请求. ...

  4. Spring Boot 结合 Redis 序列化配置的一些问题

    前言 最近在学习Spring Boot结合Redis时看了一些网上的教程,发现这些教程要么比较老,要么不知道从哪抄得,运行起来有问题.这里分享一下我最新学到的写法 默认情况下,Spring 为我们提供 ...

  5. 【Python】2.17学习笔记 移位运算符,逻辑运算符

    移位运算符 左移运算符 \(<<\),将对应的二进制数末尾补一颗零,高位自然溢出(遁入虚无 print( 5 << 2 ) 把\(5\)的二进制数左移两位 即把\(101\)变 ...

  6. 从零搭建Spring Cloud Gateway网关(二)—— 打印请求响应日志

    作为网关,日志记录是必不可少的功能,可以在网关出增加requestId来查询整个请求链的调用执行情况等等. 打印请求日志 打印请求日志最重要的就是打印请求参数这些东西,不过RequestBody通常情 ...

  7. react 脚手架装后 运行eject报错 的 正确运行方式

    git init git add . git commit -m 'init' npm run eject 或者 cnpm run eject

  8. 使用ASP.NET MVC 5快速实现芒果分销后台管理系统(前言)

    ### 前言 在前一篇文章中,我提到最近要陆续为大家写一些.Net实战技术文章.从今天起,我将围绕一个入门级现实的芒果分销管理系统案例,使用ASP.NET MVC 5,从前端到后端,一步一步为大家呈现 ...

  9. Spring扩展:替换IOC容器中的Bean组件 -- @Replace注解

    1.背景:     工作中是否有这样的场景?一个软件系统会同时有多个不同版本部署,比如我现在做的IM系统,同时又作为公司的技术输出给其他银行,不同的银行有自己的业务实现(比如登陆验证.用户信息查询等) ...

  10. Mac brew命令的使用

    mac 终端程序管理工具 能让你更快速的安装你想要的工具.而不用考虑大量的依赖. 安装brew复制下面的命令,终端执行  官网Homebrew /usr/bin/ruby -e "$(cur ...