为什么稍微延迟的线程会选择多线程 Java 程序中类的静态成员变量的最新更新值



我一直在研究下面的示例代码:

public class GlobalStatic_Multithread extends Thread{
private static int threadcounter = 0;
public void run()
{
    threadcounter++ ;
    System.out.println(threadcounter);
}
public static void main(String[] args) {
    for(int i  =0 ; i < 3 ; i++)
    {
        new GlobalStatic_Multithread().start();
    }
    System.out.println(threadcounter +  " main ending");
}



}很明显,三个线程,每个线程都有自己的锁,用于将启动的类GlobalStatic_Multithread。我的同行对这个程序的解释是,在 Thread-1 启动后,静态变量线程计数器(一个 int)将递增到值 1。在打印之前,操作系统很有可能抢占线程 1 并运行线程 2,它(线程 2)将为线程计数器保留值 1,它将递增到 2 并在线程 1 打印线程计数器的值为 2 之前打印(我的意思是线程 2 将打印)。我对这个理论非常清楚,我清楚地知道静态和非静态变量之间的区别。

有一些微妙之处,我无法理解这些线程如何获取线程计数器的值。我在这里的论点是当 Thread-1 启动时,它有自己的本地缓存,所以它应该只查看它自己的本地缓存,除非线程计数器被标记为易失性。当操作系统在打印值之前抢占线程 1 并让线程 2 运行时,线程 2 将线程计数器增加到 2。线程-1 如何获得线程计数器的值为 2 ?线程不应该从自己的缓存中获取值吗? .因为,当操作系统抢占线程 1 时,它会在自己的缓存中将线程计数器的值保存为 1,并且它应该将线程计数器打印为 1。

我缺少有关 JVM 堆和线程本地缓存的一些内容,因此我不得不发布这个问题。我知道静态成员变量与类相关联,并且它们为程序中的所有对象提供了常量值(换句话说,如果使用静态成员变量 int = 0 的类的两个对象和一个对象递增到 i++,那么第二个对象将获得更新的值,在本例中为 1)。而且我也知道静态成员变量存储在 JVM 堆中。

而且我也知道当 JVM 启动时,它会使用自己的缓存副本加载每个启动的线程,并且该缓存将保存类成员的所有值。

您是否可能来自不同的语言,静态意味着与您习惯的不同?Java 中的静态意味着类只有一个属性,因此它在实例之间共享。尝试运行它以查看它是否更有意义。

public class GlobalStatic_Multithread extends Thread{
private static int classCounter = 0;
private int instanceCounter = 0;
public void run()
{
    ++classCounter;
    ++instanceCounter;
    System.out.println("classCounter:" + classCounter + " instanceCounter:" + instanceCounter);
}
public static void main(String[] args) {
    for(int i  =0 ; i < 3 ; i++)
    {
        new GlobalStatic_Multithread().start();
    }
    System.out.println(threadcounter +  " main ending");
}

编译器和热点本机编译可以在优化方面有很大的灵活性,并且您不应该依赖于任何未为该语言指定的行为。 如果你正在做多线程编程,那么你应该编程以获得你想要的表达行为。

您现在正在做的事情不是线程安全的,并且会给出不确定的结果。

如果您希望静态字段的增量始终考虑此线程和其他线程已经进行的增量,那么您可以使用同步,但如果使用其中一个原子类会更好:

public class GlobalStatic_Multithread extends Thread{
private static AtomicInteger threadcounter = new AtomicInteger(); // defaults to 0
public void run()
{
    int incrementedValue = threadcounter.incrementAndGet() ;
    System.out.println(incrementedValue);
}
public static void main(String[] args) {
    for(int i  =0 ; i < 3 ; i++)
    {
        new GlobalStatic_Multithread().start();
    }
    System.out.println(threadcounter.get() +  " main ending");
}

此外,如果你想知道在你从main获取要打印的值之前,3个线程已经完成了它们的工作,那么你应该做这样的事情:

public class GlobalStatic_Multithread extends Thread {
private static AtomicInteger threadcounter = new AtomicInteger(); // defaults to 0
private CountDownLatch doneSignal;  // This will be passed to constructor instead of
                                    // static, to avoid race condition on construction
                                    // of object on main thread.
public GlobalStatic_Multithread(CountDownLatch doneSignal) {
    this.doneSignal = doneSignal;
}
public void run()
{
    int incrementedValue = threadcounter.incrementAndGet() ;
    System.out.println(incrementedValue);
    doneSignal.countDown();
}
public static void main(String[] args) {
    int threadsToStart = 3;
    CountDownLatch latch = new CountDownLatch(threadsToStart );
    for(int i  =0 ; i < threadsToStart  ; i++)
    {
        new GlobalStatic_Multithread(latch).start();
    }
    latch.await(); // waits for all threads to finish (latch value goes to 0)
    System.out.println(threadcounter.get() +  " main ending");
}

最新更新