【问题标题】:AtomicInteger thread safetyAtomicInteger 线程安全
【发布时间】:2021-08-25 08:21:38
【问题描述】:

我怎样才能实现这里的 while 循环总是准确执行 100 次。当我执行代码时,在极少数情况下,它会在控制台上打印 99 或 98 行,而不总是 100 行,我不明白。

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

public class Print implements Runnable {
    static AtomicInteger atomicInteger = new AtomicInteger(0);

    @Override
    public void run() {
        while (atomicInteger.getAndIncrement() < 100)
            System.out.println(Thread.currentThread());
    }

    public static void main(String[] args) throws InterruptedException {
        ArrayList<Thread> threads = new ArrayList<>();

        for (int i = 0; i < 5; i++)
            threads.add(new Thread(new Print()));

        for (Thread thread : threads)
            thread.start();

        for (Thread thread : threads)
            thread.join();
    }
}

【问题讨论】:

  • 这段代码应该可以工作,因为getAndIncrement 是线程安全的,你确定它只打印 98 或 99 次吗?
  • 谢谢卡罗尔。 IDE 或在后台运行的其他程序会影响输出吗?我试图重现该问题,但无法重现。我已经在 Excel 中复制了控制台输出,我确信它曾经是 98 行和其他 99 行。
  • 我无法重现您的问题,而且代码看起来正确,我不知道您怎么会收到 98 或 99 而不是 100。
  • 计数部分在我看来还可以。我对并行 println 调用有一点怀疑; description 建议打印值和打印行终止符是不同的操作,我想 可能 会导致两个值后跟两个换行符 - 但是你会得到明显错误的输出。

标签: java thread-safety atomicinteger


【解决方案1】:

无法复制您报告的体验。

在我的调试器中,这似乎是正确的,因为我在您的代码中看不到任何线程安全错误。

为了自动化进一步的测试,我将您的代码更改如下。我没有调用System.out.println,而是将线程ID 添加到LongList。为了线程安全,我将列表设为CopyOnWriteArrayList。我可以以编程方式检测结果列表的大小是否不完全是 100 个元素。

package work.basil.demo;

import java.time.Instant;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

public class Print implements Runnable
{
    static AtomicInteger atomicInteger = new AtomicInteger( 0 );
    static CopyOnWriteArrayList < Long > list = new CopyOnWriteArrayList <>();

    @Override
    public void run ( )
    {
        while ( atomicInteger.getAndIncrement() < 100 )
            //System.out.println(Thread.currentThread());
            list.add( Thread.currentThread().getId() );
    }

    public static void main ( String[] args ) throws InterruptedException
    {
        System.out.println( "INFO - demo starting. " + Instant.now() );
        for ( int cycle = 0 ; cycle < 1_000_000 ; cycle++ )
        {
            ArrayList < Thread > threads = new ArrayList <>();

            for ( int i = 0 ; i < 5 ; i++ )
                threads.add( new Thread( new Print() ) );

            for ( Thread thread : threads )
                thread.start();

            for ( Thread thread : threads )
                thread.join();

//            System.out.println( "list.size() = " + list.size() );
//            if ( list.size() == 100 ) { System.out.println( "DEBUG list.size() = " + ( list.size() ) ); }
            if ( list.size() != 100 ) { System.out.println( "DEBUG list.size() = " + ( list.size() ) ); }
        }
        System.out.println( "INFO - demo done. " + Instant.now() );
    }
}

在 Mac mini (2018) 3 GHz Intel Core i5 和 32 GB 2667 MHz DDR4 上运行时,在 IntelliJ 中使用 Java 16。将cycle 运行到一百万大约需要 5 分钟。

INFO - demo starting. 2021-06-08T22:11:56.010181Z
INFO - demo done. 2021-06-08T22:16:26.982616Z

ExecutorService

顺便说一句,在现代 Java 中,我们很少需要直接寻址 Thread 类。相反,请使用添加到 Java 5 的 Executors 框架。

这是上面代码的修改版本,重新调整为使用执行器服务。

public static void main ( String[] args ) throws InterruptedException
{
    System.out.println( "INFO - demo starting. " + Instant.now() );
    for ( int cycle = 0 ; cycle < 1_000_000 ; cycle++ )
    {
        ExecutorService executorService = Executors.newFixedThreadPool( 5 );

        int countTasks = 5;
        for ( int i = 0 ; i < countTasks ; i++ )
        {
            executorService.submit( new Print2() );
        }

        executorService.shutdown();
        executorService.awaitTermination( Duration.ofMinutes( 7 ).toSeconds() , TimeUnit.SECONDS );

//            System.out.println( "list.size() = " + list.size() );
//            if ( list.size() == 100 ) { System.out.println( "DEBUG list.size() = " + ( list.size() ) ); }
        if ( list.size() != 100 ) { System.out.println( "DEBUG list.size() = " + ( list.size() ) ); }
    }
    System.out.println( "INFO - demo done. " + Instant.now() );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-06
    • 1970-01-01
    • 2016-04-20
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 2017-08-23
    • 2017-04-14
    相关资源
    最近更新 更多