将AtomicInteger对象作为方法的局部变量, 传递给其他线程, 读写操作是否是线程安全的?
场景
在main线程中, 有一个方法名为triggerSomeThreadWithMethodLocalVariable
, 该方法会启动一些线程并且带着该方法里的一个类型为AtomicInteger的局部变量(一般写代码是把线程共享的变量作为类的成员变量),每个线程对该变量的局部变量atomicInteger进行写操作, atomicInteger变量是否在线程间持续可见并且线程安全?
代码
/**
* @author rhyme
*/
@Slf4j
public class MethodLocalVariableMain {
public static void main(String[] args) {
final MethodLocalVariableMain methodLocalVariableMain = new MethodLocalVariableMain();
final AtomicInteger atomicInteger = new AtomicInteger(10);
methodLocalVariableMain.triggerSomeThreadWithMethodLocalVariable(atomicInteger);
}
public void triggerSomeThreadWithMethodLocalVariable(AtomicInteger atomicInteger) {
final int threadCount = 10;
CompletableFuture[] completableFutures = new CompletableFuture[threadCount];
for (int i = 0; i < threadCount; i++) {
completableFutures[i] =
CompletableFuture.runAsync(
() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
log.error("InterruptedException happen when TimeUnit.SECONDS.sleep(3);", e);
Thread.currentThread().interrupt();
}
log.info(
"ThreadName: {}, atomicInteger.decrementAndGet(): {}.",
Thread.currentThread().getName(),
atomicInteger.decrementAndGet());
});
}
// 等待所有CompletableFuture线程执行完毕
CompletableFuture.allOf(completableFutures).join();
log.info(
"In main thread, after all completableFuture is finished, threadName: {}, atomicInteger.get(): {}.",
Thread.currentThread().getName(),
atomicInteger.get());
}
}
上述代码流程见上面的"场景"描述.
运行结果
在4核8逻辑处理测试结果如下:
ThreadName: ForkJoinPool.commonPool-worker-1, atomicInteger.decrementAndGet(): 3.
ThreadName: ForkJoinPool.commonPool-worker-7, atomicInteger.decrementAndGet(): 5.
ThreadName: ForkJoinPool.commonPool-worker-3, atomicInteger.decrementAndGet(): 6.
ThreadName: ForkJoinPool.commonPool-worker-6, atomicInteger.decrementAndGet(): 4.
ThreadName: ForkJoinPool.commonPool-worker-2, atomicInteger.decrementAndGet(): 9.
ThreadName: ForkJoinPool.commonPool-worker-5, atomicInteger.decrementAndGet(): 7.
ThreadName: ForkJoinPool.commonPool-worker-4, atomicInteger.decrementAndGet(): 8.
ThreadName: ForkJoinPool.commonPool-worker-3, atomicInteger.decrementAndGet(): 0.
ThreadName: ForkJoinPool.commonPool-worker-7, atomicInteger.decrementAndGet(): 2.
ThreadName: ForkJoinPool.commonPool-worker-1, atomicInteger.decrementAndGet(): 1.
In main thread, after all completableFuture is finished, threadName: main, atomicInteger.get(): 0.
总结
根据结果来看, 被main线程方法传递的atomicInteger变量, 它的value属性在新启动的各个线程是线程安全, 并且value持续可见;
原理应该是, 传递的是atomicInteger变量的引用, 即多个线程持有的是同一份atomicInteger变量的引用, 并且利用了AtomicInteger的特性(CAS修改被volatile修饰的变量value, 对value操作的原子性以及变量value在多线程间的可见性);
虽然结果是正确的, 但是还是不建议这样使用方法的局部变量, 这样可能会发生线程逃逸等问题;
线程之间的共享变量, 还是应该作为对象的成员变量更好, 这才是常见的规范写法.