• 将AtomicInteger对象作为方法的局部变量, 传递给其他线程, 读写操作是否是线程安全的?


    将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在多线程间的可见性);

    虽然结果是正确的, 但是还是不建议这样使用方法的局部变量, 这样可能会发生线程逃逸等问题;

    线程之间的共享变量, 还是应该作为对象的成员变量更好, 这才是常见的规范写法.

  • 相关阅读:
    个人冲刺8
    个人冲刺7
    个人冲刺6
    个人冲刺5
    个人冲刺4
    个人冲刺阶段3
    个人冲刺阶段2
    课下作业1-扩展阅读
    随手快递app开发的第四天
    随手快递app开发的第三天
  • 原文地址:https://www.cnblogs.com/theRhyme/p/14026948.html
Copyright © 2020-2023  润新知