Java多线程atomic包介绍及使用方法

引言

Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。

Atomic包介绍

在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。

原子更新基本类型类

用于通过原子的方式更新基本类型,Atomic包提供了以下三个类:

AtomicBoolean:原子更新布尔类型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新长整型。
AtomicInteger的常用方法如下:

int addAndGet(int delta) :以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果
boolean compareAndSet(int expect, int update) :如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。
int getAndIncrement():以原子方式将当前值加1,注意:这里返回的是自增前的值。
void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值                                                                                                                                                                                                    int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。

AtomicInteger例子代码如下:

import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest {
	static AtomicInteger ai = new AtomicInteger(1);
	public static void main(String[] args) {
		System.out.println(ai.getAndIncrement());
		System.out.println(ai.get());
	}
}

输出

1
2

餐后甜点

Atomic包提供了三种基本类型的原子更新,但是Java的基本类型里还有char,float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看下Unsafe的源码,发现Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double也可以用类似的思路来实现。

原子更新数组类

通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:

AtomicIntegerArray:原子更新整型数组里的元素。
AtomicLongArray:原子更新长整型数组里的元素。
AtomicReferenceArray:原子更新引用类型数组里的元素。

AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,其常用方法如下

int addAndGet(int i, int delta):以原子方式将输入值与数组中索引i的元素相加。
boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。

实例代码如下:

public class AtomicIntegerArrayTest {
	static int[] value = new int[] { 1, 2 };
	static AtomicIntegerArray ai = new AtomicIntegerArray(value);
	public static void main(String[] args) {
		ai.getAndSet(0, 3);
		System.out.println(ai.get(0));
		System.out.println(value[0]);
	}
}

输出

3
1

AtomicIntegerArray类需要注意的是,数组value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,所以当AtomicIntegerArray对内部的数组元素进行修改时,不会影响到传入的数组。

原子更新引用类型

原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类:

AtomicReference:原子更新引用类型。
AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
AtomicReference的使用例子代码如下:

public class AtomicReferenceTest {
	public static AtomicReference<user> atomicUserRef = new AtomicReference</user><user>();
	public static void main(String[] args) {
		User user = new User("conan", 15);
		atomicUserRef.set(user);
		User updateUser = new User("Shinichi", 17);
		atomicUserRef.compareAndSet(user, updateUser);
		System.out.println(atomicUserRef.get().getName());
		System.out.println(atomicUserRef.get().getOld());
	}
	static class User {
		private String name;
		private int old;
		public User(String name, int old) {
			this.name = name;
			this.old = old;
		}
		public String getName() {
			return name;
		}
		public int getOld() {
			return old;
		}
	}
}

输出

Shinichi
17

原子更新字段类

如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:

AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicLongFieldUpdater:原子更新长整型字段的更新器。
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。

原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。

第一步,因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法AtomicIntegerFieldUpdater.newUpdater创建一个更新器,并且需要设置想要更新的类和属性。第二步,更新类的字段(属性)必须使用public volatile修饰符(private volatile int old)

AtomicIntegerFieldUpdater的例子代码如下:

public class AtomicIntegerFieldUpdaterTest {
	private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater
				.newUpdater(User.class, "old");
	public static void main(String[] args) {
		User conan = new User("conan", 10);
		System.out.println(a.getAndIncrement(conan));
		System.out.println(a.get(conan));
	}
	public static class User {
		private String name;
		public volatile int old;
		public User(String name, int old) {
			this.name = name;
			this.old = old;
		}
		public String getName() {
			return name;
		}
		public int getOld() {
			return old;
		}
	}
}

输出

10
11

总结

以上就是本文关于Java多线程atomic包介绍及使用方法的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

Java多线程中不同条件下编写生产消费者模型方法介绍

java多线程编程同步器Future和FutureTask解析及代码示例

Java多线程之显示锁和内置锁总结详解

如有不足之处,欢迎留言指出。

(0)

相关推荐

  • java多线程和并发包入门示例

    一.java多线程基本入门java多线程编程还是比较重要的,在实际业务开发中经常要遇到这个问题. java多线程,传统创建线程的方式有两种. 1.继承自Thread类,覆写run方法. 2.实现Runnable接口,实现run方法. 启动线程的方法都是调用start方法,真正执行调用的是run方法.参考代码如下: 复制代码 代码如下: package com.jack.thread; /** * 线程简单演示例子程序 *  * @author pinefantasy * @since 2013-

  • Java多线程atomic包介绍及使用方法

    引言 Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作.原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞. Atomic包介绍 在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段.Atomic包里的类基本都是使用Unsafe实现的包装类. 原子

  • Java多线程Atomic包操作原子变量与原子类详解

    在阅读这篇文章之前,大家可以先看下<Java多线程atomic包介绍及使用方法>,了解atomic包的相关内容. 一.何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中,是不能被中断的.通常来说,原子指令由硬件提供,供软件来实现原子方法(某个线程进入该方法后,就不会被中断,直到其执行完成) 在x86平台上,CPU提供了在指令执行期间对总线加锁的手段.

  • Java 多线程有序执行的几种方法总结

    Java 多线程有序执行的几种方法总结 同事无意间提出了这个问题,亲自实践了两种方法.当然肯定还会有更多更好的方法. 方法一 import java.util.concurrent.atomic.AtomicInteger; public class OrderedThread1 { static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws Interrupte

  • java 中的封装介绍及使用方法

    java 中的封装介绍及使用方法 在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装.隐藏起来的方法. 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问. 要访问该类的代码和数据,必须通过严格的接口控制. 封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段. 适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性. 封装的优点 1. 良好的封装能够减少耦合. 2. 类内部

  • Java中Arrays的介绍及使用方法示例

    arrays介绍 java.util.Arrays是一个与数组相关的工具类,里面提供了大量的静态的方法,用来实现数组常见的操作. public static String toString(数组):将参数数组编程字符串(按照默认的格式:{元素1.元素2.元素3-}) public static Void sort(数组):按照默认升序(从小到大)对数组元素进行排序 备注: 1.如果是数值的话,sort默认按照升序从小到大 2.如果是字符串,sort按照字母升序排列 3.如果是自定义类型,那么自定

  • Java多线程的具体介绍与使用笔记小结

    一.基本概念:线程.进程 1.1.进程与线程的具体介绍 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径. 若一个进程同一时间并行执行多个线程,就是支持多线程的 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小 一个进程中的多个线程共享相同的内存单元/内存地址空间à它们从同一堆中分配对象,可以访问相同的变量和对象.这就使得线程间通信更简便.高效.但多个线程操作共享的系统资源可能就会带来安全的隐患. 进程(process),是程序的

  • 浅谈java 执行jar包中的main方法

    浅谈java 执行jar包中的main方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar 执行后总是运行指定的主方法,如果 jar 中有多个 main 方法,那么如何运行指定的 main 方法呢? 用下面的命令试试看: java -classpath ****.jar ****.****.className [args] "****.****"表示"包名": "className"表示&

  • java多线程读取多个文件的方法

    本文实例为大家分享了java多线程读取多个文件的具体代码,供大家参考,具体内容如下 工具类代码如下: import java.io.*; import java.util.List; import java.util.concurrent.CountDownLatch; /** * 多线程读取多个文件 */ public class FileThread extends Thread{ private final CountDownLatch countDownLatch = new Count

  • Java多线程ThreadAPI详细介绍

    1.Thread的构造方法 package threadAPI; public class CreateThread { public static void main(String[] args) { Thread t1 = new Thread(); Thread t2 = new Thread(); t1.start(); t2.start(); System.out.println(t1.getName()); System.out.println(t2.getName()); } }

  • java使用common-httpclient包实现post请求方法示例

    前言 项目中需要请求第三方接口,而且要求请求参数数据为json类型的.本来首先使用的是httpclient的jar包,但是因为项目中已经使用了common-httpclient的jar包,引起了冲突,所以不得不使用common-httpclient来实现. 示例代码: import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStr

随机推荐