如何在多线程程序中保存线程变量的值

How to preserve the value of thread variable in a multi-threaded program

我有一个代码,它每 5 秒生成一个新线程,每个线程定期打印出计数器的值。它总共启动了 10 个线程。

代码如下:

public static void main(String[] args) {
    ScheduledExecutorService task = Executors.newScheduledThreadPool(10);
    AtomicInteger counter = new AtomicInteger(0);
    int i = 0;
    while (i < 10) {
        // sleep 5 seconds
        sleep(5);
        task.scheduleAtFixedRate(() -> {
            String threadName = Thread.currentThread().getName();
            int newCounter = counter.get();
            System.out.println(String.format("[%s] counter = %d" , threadName, newCounter));
        }, 0, 2, TimeUnit.SECONDS);

        counter.getAndIncrement();
        ++i;
    }
}

private static void sleep(int sleepTime) {
    try {
        TimeUnit.SECONDS.sleep(sleepTime);
    } catch (InterruptedException ie) {

    }
}

这是输出:

[pool-1-thread-1] counter = 1
[pool-1-thread-1] counter = 1
[pool-1-thread-2] counter = 1
[pool-1-thread-3] counter = 1
[pool-1-thread-1] counter = 2
[pool-1-thread-2] counter = 2
[pool-1-thread-4] counter = 2
[pool-1-thread-5] counter = 2
[pool-1-thread-3] counter = 2
[pool-1-thread-1] counter = 3
[pool-1-thread-7] counter = 3
[pool-1-thread-6] counter = 3
[pool-1-thread-8] counter = 3
[pool-1-thread-4] counter = 3
[pool-1-thread-9] counter = 3
[pool-1-thread-5] counter = 3
[pool-1-thread-10] counter = 3
[pool-1-thread-1] counter = 4
   .......

我希望每个线程都打印出它的原始值,例如,pool-1-thread-1 应该始终打印出 0 或 1(取决于计数器是先递增还是线程先启动),第二个线程应该持续发出前一个计数器 + 1,...如下所示:

[pool-1-thread-1] counter = 1
[pool-1-thread-1] counter = 1
[pool-1-thread-2] counter = 2
[pool-1-thread-3] counter = 3
[pool-1-thread-1] counter = 1
[pool-1-thread-2] counter = 2
[pool-1-thread-4] counter = 4
[pool-1-thread-5] counter = 5
[pool-1-thread-3] counter = 3
[pool-1-thread-1] counter = 1
[pool-1-thread-7] counter = 7
  .......

我的问题是如何使 newCounter 局部变量在每个线程中保持其原始值。我能想到的方法是将线程名称到它的原始值的映射存储在一个线程安全的散列table中。有没有更清洁的方法来完成这个?谢谢

要将值绑定到特定线程,您可以使用 ThreadLocal 变量。 javadoc 中的 ThreadId 示例 class 可以轻松调整以解决您的用例。

public static void main(String[] args) {
    ScheduledExecutorService task = Executors.newScheduledThreadPool(10);
    AtomicInteger counter = new AtomicInteger(0);
    ThreadLocal<Integer> id = ThreadLocal.withInitial(counter::incrementAndGet);
    int i = 0;
    while (i < 10) {
        // sleep 5 seconds
        sleep(5);
        task.scheduleAtFixedRate(() -> {
            String threadName = Thread.currentThread().getName();
            System.out.println(String.format("[%s] counter = %d" , threadName, id.get()));
        }, 0, 2, TimeUnit.SECONDS);

        ++i;
    }
}

private static void sleep(int sleepTime) {
    try {
        TimeUnit.SECONDS.sleep(sleepTime);
    } catch (InterruptedException ie) {

    }
}