将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在多线程间的可见性);
虽然结果是正确的, 但是还是不建议这样使用方法的局部变量, 这样可能会发生线程逃逸等问题;
线程之间的共享变量, 还是应该作为对象的成员变量更好, 这才是常见的规范写法.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架