java中的Atomic类
java中的Atomic类
问题背景
在多线程环境中,我们最常遇到的问题就是变量的值进行同步。因为变量需要在多线程中进行共享,所以我们必须需要采用一定的同步机制来进行控制。
通过之前的文章,我们知道可以采用Lock的机制,当然也包括今天我们讲的Atomic类。
下面我们从两种方式来分别介绍。
Lock
在之前的文章中,我们也讲了同步的问题,我们再回顾一下。 如果定义了一个计数器如下:
public class Counter {
int counter;
public void increment() {
counter++;
}
}
如果是在单线程环境中,上面的代码没有任何问题。但是如果在多线程环境中,counter++将会得到不同的结果。
因为虽然counter++看起来是一个原子操作,但是它实际上包含了三个操作:读数据,加一,写回数据。
我们之前的文章也讲了,如何解决这个问题:
public class LockCounter {
private volatile int counter;
public synchronized void increment() {
counter++;
}
}
通过加synchronized,保证同一时间只会有一个线程去读写counter变量。
通过volatile,保证所有的数据直接操作的主缓存,而不使用线程缓存。
这样虽然解决了问题,但是性能可能会受影响,因为synchronized会锁住整个LockCounter实例。
使用Atomic
通过引入低级别的原子化语义命令(比如compare-and-swap (CAS)),从而能在保证效率的同时保证原子性。
一个标准的CAS包含三个操作:
- 将要操作的内存地址M。
- 现有的变量A。
- 新的需要存储的变量B。
CAS将会先比较A和M中存储的值是否一致,一致则表示其他线程未对该变量进行修改,则将其替换为B。 否则不做任何操作。
使用CAS可以不用阻塞其他的线程,但是我们需要自己处理好当更新失败的情况下的业务逻辑处理情况。
Java提供了很多Atomic类,最常用的包括AtomicInteger, AtomicLong, AtomicBoolean, 和 AtomicReference.
其中的主要方法:
- get() – 直接中主内存中读取变量的值,类似于volatile变量。
- set() – 将变量写回主内存。类似于volatile变量。
- lazySet() – 延迟写回主内存。一种常用的情景是将引用重置为null的情况。
- compareAndSet() – 执行CAS操作,成功返回true,失败返回false。
- weakCompareAndSet() – 比较弱的CAS操作,不同的是它不执行happens-before操作,从而不保证能够读取到其他变量最新的值。
我们看下怎么用:
public class AtomicCounter {
private final AtomicInteger counter = new AtomicInteger(0);
public int getValue() {
return counter.get();
}
public void increment() {
while(true) {
int existingValue = getValue();
int newValue = existingValue + 1;
if(counter.compareAndSet(existingValue, newValue)) {
return;
}
}
}
}
本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/atomic
更多教程请参考 flydean的博客
java中的Atomic类的更多相关文章
- 【Java并发】Java中的原子操作类
综述 JDK从1.5开始提供了java.util.concurrent.atomic包. 通过包中的原子操作类能够线程安全地更新一个变量. 包含4种类型的原子更新方式:基本类型.数组.引用.对象中字段 ...
- java中的原子操作类AtomicInteger及其实现原理
/** * 一,AtomicInteger 是如何实现原子操作的呢? * * 我们先来看一下getAndIncrement的源代码: * public final int getAndIncremen ...
- 带有静态方法的类(java中的math类)
带有静态方法的类通常(虽然不一定是这样)不打算被初始化. 可以用私有构造函数来限制非抽象类被初始化. 例如,java中的math类.它让构造函数标记为私有,所以你无法创建Math的实例.但Math类却 ...
- java中的File类
File类 java中的File类其实和文件并没有多大关系,它更像一个对文件路径描述的类.它即可以代表某个路径下的特定文件,也可以用来表示该路径的下的所有文件,所以我们不要被它的表象所迷惑.对文件的真 ...
- Java基础(43):Java中的Object类与其方法(转)
Object类 java.lang.Object java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. O ...
- java中基于TaskEngine类封装实现定时任务
主要包括如下几个类: 文章标题:java中基于TaskEngine类封装实现定时任务 文章地址: http://blog.csdn.net/5iasp/article/details/10950529 ...
- Java中的Unsafe类111
1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...
- Java中遍历实体类(处理MongoDB)
在实际过程中,经常要将实体类进行封装,尤其是处理数据库的过程中:因此,对于遍历实体类能够与数据库中的一行数据对应起来. 我是使用的环境是Spring boot,访问的数据库时MongoDB 实体类遍历 ...
- java中遍历实体类,获取属性名和属性值
方式一(实体类): //java中遍历实体类,获取属性名和属性值 public static void testReflect(Object model) throws Exception{ for ...
随机推荐
- java web利用mvc结构实现简单聊天室功能
简单聊天室采用各种内部对象不适用数据库实现. 一个聊天室要实现的基本功能是: 1.用户登录进入聊天室, 2.用户发言 3.用户可以看见别人发言 刚才算是简单的需求分析了,现在就应该是进 ...
- wifi无线桥接
考虑到不同路由器配置上或许有细微差别,我此处路由器是水星(牌子)路由器. 首先需要2台路由器,一台已经能够上网,作为主路由器:另一台啥都没有配置,将来用作副路由器,与主路由器桥接. 步骤: 获取主路由 ...
- 开学java的初步考试
//第一个.java文件 package project1; //20183777 温学智 信1805-2 public class ScoreInformation { private String ...
- C语言 文件操作(三)
1.fputs() int fputs(const char *s, FILE *stream); s 代表要输出的字符串的首地址,可以是字符数组名或字符指针变量名. stream 表示向何种流中输出 ...
- JQUERY滚动加载
$(document).height():整个网页的高度$(window).height():浏览器可视窗口的高度$(window).scrollTop():浏览器可视窗口顶端距离网页顶端的高度(垂直 ...
- hive常用函数五
复合类型构建操作 1. Map类型构建: map 语法: map (key1, value1, key2, value2, …) 说明:根据输入的key和value对构建map类型 举例: hive& ...
- Linux c++ vim环境搭建系列(0)——简介
vim 学习 简介: 源码编译使用vim及其插件. 内容包含: vim的编译安装, llvm clang的编译安装, 插件youcompleteme的编译安装使用, 以及vim其他插件的使用. 搭建环 ...
- public、private、protected继承区别
- 跨域cookies 共享
这是由于,本地调试.涉及到cookies的问题 想要跨域使用的问题 vue 中的mian.js中放入下面代码 import axios from 'axios' axios.defaults.with ...
- C - Sigma Function LightOJ - 1336 (数论)
一个让人脑洞大开的题. 题目大意比较简单,询问[1,n]有多少个数其因子和为偶数. 因子分解定理中求因子和的公式是 f(n)=(1+p1+p1^2+p1^3+...+p1^a1)(1+p2+p2^2+ ...