將AtomicInteger物件作為方法的區域性變數, 傳遞給其他執行緒, 讀寫操作是否是執行緒安全的?
阿新 • • 發佈:2020-11-23
將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在多執行緒間的可見性);
雖然結果是正確的, 但是還是不建議這樣使用方法的區域性變數, 這樣可能會發生執行緒逃逸等問題;
執行緒之間的共享變數, 還是應該作為物件的成員變數