1.什么是Atomic?
Atomic,中文意思是“原子的”,在java多线程中,有这样的一个包:

java.util.concurrent.atomic——线程安全的原子操作包

这是JDK1.5的版本中为我们提供的原子操作包。所谓“原子”操作,是指一组不可分割的操作,操作者对目标对象进行操作时,要么完成所有操作后其他操作者才能操作;要么这个操作者不能进行任何操作。

2.Atomic有什么作用?
原子操作包为我们提供了四类原子操作:

  • 原子更新基本类型
  • 原子更新数组
  • 原子更新引用
  • 原子更新字段。

通过这四类,可以有效地解决实际开发中遇到脏数据的问题。

3.原子更新基本类型
原子更新基本类型中有三种操作:

  • AtomicBoolean:布尔数据的原子操作
  • AtomicInteger:整型数字的原子操作
  • AtomicLong:长整型数字的原子操作

下面通过一段代码实现一个AtomicInteger的累加器:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {
public static void main(String[] args) {
Add add = new Add();
Thread t1 = new Thread(add);
Thread t2 = new Thread(add);
t1.start();
t2.start();
}
} class Add implements Runnable {
AtomicInteger count = new AtomicInteger(0); @Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + " " + count.incrementAndGet());
}
}
}

从上面的代码我们可以看出,这里并没有使用synchronized就实现了线程安全,这是为什么呢?
我们不妨看一下源码

public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

那源码中为什么是一个死循环呢?
这里要引出两个概念——悲观锁和乐观锁
悲观锁是一种独占锁,它假设的前提是“冲突一定会发生”,所以处理某段可能出现数据冲突的代码时,这个代码段就要被某个线程独占。而独占意味着“其它即将执行这段代码的其他线程”都将进入“阻塞”/“挂起”状态。synchronized关键字就是java对于悲观锁的实现。
乐观锁假定“冲突不一定会出现”,如果出现冲突则进行重试,直到冲突消失。 由于乐观锁的假定条件,所以乐观锁不会独占资源,性能自然在多数情况下就会好于悲观锁,但也会有少数情况坏于悲观锁,就是一旦多线程对某个资源的抢占频度达到了某种规模,就会导致乐观锁内部出现多次更新失败的情况,最终造成乐观锁内部进入一种“活锁”状态。AtomicInteger是一个标准的乐观锁实现。

所以说在incrementAndGet()中,会有一个“死循环”,这是incrementAndGet()方法会有“比较-重试”的过程。

4.原子更新数组

  • AtomicIntegerArray:原子操作整型数组
  • AtomicLongArray:原子操作长整型数组
  • AtomicReferenceArray:原子操作对象引用数组

下面通过一段代码实现一个AtomicIntegerArray的累加器:

import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
AddTest add = new AddTest();
Thread[] t = new Thread[10];
for (int i = 0; i < 10; i++) {
t[i] = new Thread(add);
}
for (int i = 0; i < 10; i++) {
t[i].start();
}
for (int i = 0; i < 10; i++) {
try {
t[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(add.arr);
} static class AddTest implements Runnable {
AtomicIntegerArray arr = new AtomicIntegerArray(10); @Override
public void run() {
for (int i = 0; i < 10000; i++) {
arr.getAndIncrement(i % arr.length());
}
}
}
}

我们不妨看一下输出:

由此可知,AtomicIntegerArray是线程安全的。

5.原子更新引用及字段
原子更新对象和字段的操作有:

  • AtomicIntegerFieldUpdater:整型数据字段更新器
  • AtomicLongFieldUpdater:长型数据字段更新器
  • AtomicReferenceFieldUpdater:对象数据字段更新器
  • AtomicReference:对象原子操作

我们用AtomicReferenceFieldUpdater更新字段操作:

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class AtomicReferenceUpdaterDemo {
public static void main(String[] args) {
Student student = new Student();
student.setName("StudentA");
Score score = new Score();
score.setScore(80);
student.setScore(score);
Score newScore = new Score();
newScore.setScore(90);
student.setScore(newScore);
System.out.println(student.getName() + ":旧成绩" + score.getScore() + " 新成绩:" + newScore.getScore());
}
} class Student {
private String name;
private volatile Score score; public Student(String name, Score score) {
super();
this.name = name;
this.score = score;
} public Student() {
super();
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Score getScore() {
return score;
} public void setScore(Score score) {
score_update.set(this, score);
} public AtomicReferenceFieldUpdater<Student, Score> score_update = AtomicReferenceFieldUpdater
.newUpdater(Student.class, Score.class, "score");
} class Score {
private int score; public Score() {
super();
} public Score(int score) {
super();
this.score = score;
} public int getScore() {
return score;
} public void setScore(int score) {
this.score = score;
}
}

输出:

下面我们来看一段关于AtomicReference的代码(实现自动充值,当少于20元时,充值20元,再进行消费,每次消费10元):

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceDemo {
static AtomicReference<Integer> money = new AtomicReference<Integer>(19); public static void main(String[] args) {
Add add = new Add();
Thread thread1 = new Thread(add);
thread1.start();
Reduce reduce = new Reduce();
Thread thread2 = new Thread(reduce);
thread2.start();
} static class Add implements Runnable { @Override
public void run() {
while (true) {
while (true) {
Integer m = money.get();
if (m < 20) {
if (money.compareAndSet(m, m + 20)) {
System.out.println("充值成功,余额:" + money.get() + "元");
break;
}
} else {
break;
}
}
}
}
} static class Reduce implements Runnable { @Override
public void run() {
while (true) {
while (true) {
Integer m = money.get();
if (m > 10) {
if (money.compareAndSet(m, m - 10)) {
System.out.println("成功消费10元,余额:" + money.get() + "元");
break;
}
} else {
break;
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

它的结果是这样的:

其实这种方法还是可能会出现cas无法判断数据状态的情况,虽然概率不大,但是一般也不采用这种方法。这儿我们先不着急,在我CAS算法和ABA问题的博客中,我们会引出这种现象以及解决方案。

最后,补充两点:

(1)CAS如果长时间不成功,会极大的增加CPU的开销。所以CAS不适合用在复杂的场景。

(2)AtomicIntegerFieldUpdater只能更新int型的数据,并不能Integer数据,要更新Integer数据要使用AtomicReferenceFieldUpdater。

Java多线程_Atomic的更多相关文章

  1. 40个Java多线程问题总结

    前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...

  2. Java多线程基础知识篇

    这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...

  3. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  4. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

  5. Java多线程--让主线程等待子线程执行完毕

    使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待. java.util.concurrent.CountDownLatch 使用c ...

  6. Java多线程 2 线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

  7. java 多线程 1 线程 进程

    Java多线程(一).多线程的基本概念和使用 2012-09-10 16:06 5108人阅读 评论(0) 收藏 举报  分类: javaSE综合知识点(14)  版权声明:本文为博主原创文章,未经博 ...

  8. 一起阅读《Java多线程编程核心技术》

    目录 第一章 Java多线程技能 (待续...)

  9. 第一章 Java多线程技能

    1.初步了解"进程"."线程"."多线程" 说到多线程,大多都会联系到"进程"和"线程".那么这两者 ...

随机推荐

  1. Spring学习之动态代理的简单实现

    先说一下代理模式的好处 1.可以使真实角色的操作更加纯粹,不用去关注一些公共的业务 2.公共的交给代理角色,实现了业务的分工 3.公共业务发生扩展的时候,方便集中管理 静态代理模式的缺点 1.一个真实 ...

  2. PHP 5 echo 和 print 语句

    PHP 5 echo 和 print 语句 在 PHP 中有两个基本的输出方式: echo 和 print. 本章节中我们会详细讨论两个语句的用法,并在实例中演示如何使用 echo 和 print. ...

  3. PHP imagecolorallocatealpha - 为一幅图像分配颜色和透明度

    imagecolorallocatealpha — 为一幅图像分配颜色和透明度.高佣联盟 www.cgewang.com 语法 int imagecolorallocatealpha ( resour ...

  4. ElasticSearch 基本概念 and 索引操作 and 文档操作 and 批量操作 and 结构化查询 and 过滤查询

    基本概念 索引: 类似于MySQL的表.索引的结构为全文搜索作准备,不存储原始的数据. 索引可以做分布式.每一个索引有一个或者多个分片 shard.每一个分片可以有多个副本 replica. 文档: ...

  5. [转]Java死锁排查

    文章来源:微信公众号:猿天地 1. 死锁的概念: 是Java多线程情况下,两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,它们都讲无法推进下去.此时称 ...

  6. iOS CALayer 简单介绍

    https://www.jianshu.com/p/09f4e36afd66 什么是CALayer: 总结:能看到的都是uiview,uiview能显示在屏幕上是因为它内部的一个层calyer层. 在 ...

  7. 1、Java 开发环境配置

    Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境. Windows 上安装开发环境 Linux 上安装开发环境 安装 Eclipse 运行 Java window系统安装ja ...

  8. PHP基础之排序

    前言 之前简单介绍了流程控制,函数,数组等.有兴趣的可以看看. PHP入门之类型与运算符 PHP入门之流程控制 PHP入门之函数 PHP入门之数组 接下来介绍一下排序,排序是将一组数据,依指定的顺序进 ...

  9. Bytom侧链Vapor源码浅析-节点出块过程

    Bytom侧链Vapor源码浅析-节点出块过程 在这篇文章中,作者将从Vapor节点的创建开始,进而拓展讲解Vapor节点出块过程中所涉及的源码. 做为Vapor源码解析系列的第一篇,本文首先对Vap ...

  10. Azure Application Gateway(一)对后端 Web App 进行负载均衡

    一,引言 今天,我们学习一个新的知识点-----Azure Application Gateway,通过Azure 应用程序网关为我么后端的服务提供负载均衡的功能.我们再文章头中大概先了解一下什么是应 ...