问题描述
我怎样才能实现这里的 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();
}
}
解决方法
无法复制您报告的体验。
在我的调试器中,这似乎是正确的,因为我在您的代码中看不到任何线程安全错误。
为了自动化进一步的测试,我对您的代码进行了如下更改。我没有调用 System.out.println
,而是将线程 ID 添加到 List
的 Long
。为了线程安全,我将列表设为 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 上运行时,该 i5 具有六个真实内核且无超线程,以及 32 GB 2667 MHz DDR4,在 IntelliJ 中使用 Java 16。运行 cycle
到 100 万大约需要 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() );
}