您的位置:首页 > 编程语言 > Java开发

java多线程[11]:原子操作(atomic)

2017-12-25 21:18 330 查看
原子操作指的是一个不可分割的操作,例如,读取是原子的,写入是原子的,但读取后加一再写入就不是原子的。多线程环境下,就有可能出现多个线程同时读取并修改一个公共资源的情况,如果[读取并修改]不是原子操作的话,就有可能会导致错误的结果。举例说明:假设多个线程同时操作一个累加器,累加器就是对一个公共字段就行读取后再加一写入,如果两个线程同时读取到的值都是10,加一后又同时写入了11,就会造成结果的偏差,因为预期的结果是12。
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,这个是我们期望的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息