java多线程[11]:原子操作(atomic)
2017-12-25 21:18
330 查看
原子操作指的是一个不可分割的操作,例如,读取是原子的,写入是原子的,但读取后加一再写入就不是原子的。多线程环境下,就有可能出现多个线程同时读取并修改一个公共资源的情况,如果[读取并修改]不是原子操作的话,就有可能会导致错误的结果。举例说明:假设多个线程同时操作一个累加器,累加器就是对一个公共字段就行读取后再加一写入,如果两个线程同时读取到的值都是10,加一后又同时写入了11,就会造成结果的偏差,因为预期的结果是12。
例如,
上面的几个类都是用来对一个变量进行原子操作的,如果希望对几个类型相同的变量同时进行原子操作的话,可以使用它们对应的数组类型,例如
下面来看一个例子:有两个公共资源,一个是普通类型
首先来看公共资源类
下面是操作普通类型
这个线程类对
下面是操作原子类型的线程类
这个类使用
下面是main方法所在的类
上面的代码使用线程池创建了20000个线程,包含10000个
最终输出的结果是:
其中,
java.util.concurrent.atomic包中提供了几个类来专门解决这类的问题。
例如,
AtomicInteger类是为了解决对int类型字段的原子读写问题,
AtomicLong是为了long类型字段的原子读写问题,当然还有
AtomicBoolean。这几个都是原始类型。
上面的几个类都是用来对一个变量进行原子操作的,如果希望对几个类型相同的变量同时进行原子操作的话,可以使用它们对应的数组类型,例如
AtomicIntegerArray,
AtomicBoolean等。
下面来看一个例子:有两个公共资源,一个是普通类型
Integer,一个是原子类型
AtomicInteger,然后有两个线程类分别以不同的方式来操作这两个公共资源(其实都是累加),在并发情况下,普通类型的结果有可能会出现错误,而原子类型的结果总是正确地。
首先来看公共资源类
import java.util.concurrent.atomic.AtomicInteger; public class SharedResources { public static int sum = 0; public static AtomicInteger atomicSum = new AtomicInteger(0); }
下面是操作普通类型
sum的线程类
import java.util.concurrent.CountDownLatch; public class Accumulator implements Runnable { CountDownLatch countDownLatch; public Accumulator(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { SharedResources.sum += 1; countDownLatch.countDown(); } }
这个线程类对
SharedResources.sum进行加一操作。
下面是操作原子类型的线程类
import java.util.concurrent.CountDownLatch; public class AtomicAccumulator implements Runnable { CountDownLatch countDownLatch; public AtomicAccumulator(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { SharedResources.atomicSum.addAndGet(1); countDownLatch.countDown(); } }
这个类使用
AtomicInteger的
addAndGet()方法,也对其进行加一操作。
下面是main方法所在的类
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class AtomicDemo { public static void main(String[] args) throws InterruptedException { int threadCount = 10000; CountDownLatch countDownLatch = new CountDownLatch(threadCount); ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < threadCount; i++) { executorService.execute(new Accumulator(countDownLatch)); executorService.execute(new AtomicAccumulator(countDownLatch)); } countDownLatch.await(); System.out.println("SharedResources.sum = " + SharedResources.sum); System.out.println("SharedResources.atomicSum = " + SharedResources.atomicSum.get()); executorService.shutdown(); } }
上面的代码使用线程池创建了20000个线程,包含10000个
Accumulator线程和10000个
AtomicAccumulator线程。使用
CountDownLatch来保证20000个线程运行结束后再输出结果。
最终输出的结果是:
SharedResources.sum = 9992 SharedResources.atomicSum = 10000
其中,
SharedResources.sum的值每次可能都不一样,也就意味着并发情况下这个值经常会出错,而
SharedResources.atomicSum每次都是10000,这个是我们期望的结果。
相关文章推荐
- Java 多线程下race condition/同步/原子操作问题
- 【Java多线程之Atomic:原子变量与原子类】
- 多线程-Java原子变量-java.util.concurrent.atomic.*
- Java多线程系列--“JUC原子类”05之 AtomicLongFieldUpdater原子类
- Java学习笔记—多线程(原子类,java.util.concurrent.atomic包,转载)
- java 多线程系列---JUC原子类(四)之AtomicReference原子类
- Java多线程系列--“JUC原子类”05之 AtomicLongFieldUpdater原子类
- [Java多线程]-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
- Java多线程——非原子64位操作(long,double)
- java.util.concurrent(JUC)的研究--》atomic原子操作--》happens-before法则
- Java多线程--原子操作的原理
- java多线程之synchornized原理以及原子操作探究学习
- Java原子操作AtomicInteger的用法
- Java多线程(二)之Atomic:原子变量与原子类
- Java多线程系列--“JUC原子类”04之 AtomicReference原子类
- Java多线程(二)之Atomic:原子变量与原子类
- JAVA多线程中,原子操作的概念——原子操作真的不需要进行同步控制吗?
- java i++ 并非原子操作的解决方法——用AtomicInteger
- C++11多线程(十二):《atomic 类型详解四:C 风格原子操作介绍》
- java.util.concurrent.atomic原子操作