【问题标题】:multi-threading: thread gets own copy of method, but parameter is shared多线程:线程获取自己的方法副本,但参数是共享的
【发布时间】:2014-09-02 22:51:48
【问题描述】:

我想确认我对此的理解-

public class Main {

    private static int j = 0;
    private int k = 0;

    public static void main(String[] args) {
        Main obj = new Main();
        obj.doProcess();
    }

    public void doProcess() {
        ExecutorService service = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 4; i++) {
            service.submit(new SingleProcessor());
        }
    }

    public static void myStaticMethod() {
        System.out.println("my static method");
        int i = 0;
        i++;
        j++;
        System.out.println("i " + i);
        System.out.println("j " + j);
    }

    public void myInstanceMethod() {
        System.out.println("my instance method");
        int i = 0;
        i++;
        k++;
        System.out.println("k " + k);
    }

    private class SingleProcessor implements Runnable {

        @Override
        public void run() {
            System.out.println("single run starts" + Thread.currentThread().getName());
            myStaticMethod();
            myInstanceMethod();
        }

    }
}

当线程运行时,它会获得自己的方法副本,无论是静态方法还是实例方法 - 在这些方法中创建的任何变量都是本地的并且特定于该线程。就像这个方法的多个“实例”同时执行一样,内部创建的任何变量都不是共享的(它是本地的)。

但是参数(静态或实例)由线程共享。

所以在上面的例子中 - i 是本地的并且特定于线程。 j 是共享的。 k 是共享的。

输出 -

single run startspool-1-thread-1
single run startspool-1-thread-2
my static method
single run startspool-1-thread-3
my static method
i 1
i 1
j 2
j 2
my instance method
my instance method
k 1
k 2
my static method
i 1
j 3
my instance method
k 3
single run startspool-1-thread-4
my static method
i 1
j 4
my instance method
k 4

我的理解是 100% 正确的吗?有人愿意用更好的词来形容吗?

谢谢。

【问题讨论】:

    标签: java multithreading concurrency static-members instance-variables


    【解决方案1】:

    如果我正确地遵循 - 那应该是正确的。

    这是对堆栈与堆的基本理解。在堆栈上声明的任何东西(在函数中声明的变量)都只能在本地使用。在堆上声明的变量可以全局访问,也可以由其范围内的任何函数访问。

    因此,如果您有一个线程处理函数,则函数内的所有变量都只能在该函数内访问。但是,如果您有一个全局变量和两个线程,则两个线程都可以访问该变量。问题是,如果它们写入该变量,您必须确保它们不会相互覆盖。

    解决方案是在一个线程读取/写入时锁定堆变量,然后在完成后解锁它。

    【讨论】:

      【解决方案2】:

      如果您运行下面的代码,您认为会发生什么?您可能会看到类似的输出,但顺序可能不同。 i 是静态变量,在静态范围内,j 在方法范围内,kMain 的实例变量。与线程相同的东西。范围与线程的工作方式相同。您应该改为阅读内部类。

      public class Main {
      
          private static int j = 0;
          private int k = 0;
      
          public static void main(String[] args) {
              Main obj = new Main();
              obj.doProcess();
          }
      
          public void doProcess() {
              for (int i = 0; i < 4; i++) {
                  SingleProcessor sp = new SingleProcessor();
                  sp.run();
              }
          }
      
          public static void myStaticMethod() {
              System.out.println("my static method");
              int i = 0;
              i++;
              j++;
              System.out.println("i " + i);
              System.out.println("j " + j);
          }
      
          public void myInstanceMethod() {
              System.out.println("my instance method");
              int i = 0;
              i++;
              k++;
              System.out.println("k " + k);
          }
      
          private class SingleProcessor implements Runnable {
      
              @Override
              public void run() {
                  System.out.println("single run starts" + Thread.currentThread().getName());
                  myStaticMethod();
                  myInstanceMethod();
              }
      
          }
      }
      

      【讨论】:

        【解决方案3】:

        你是对的。

        静态变量可以在您的应用程序中被解释为单例,无论您有多少线程,它们都将始终引用同一个变量。

        至于您的实例变量 'k',它之所以有效,是因为您的嵌套类不是静态的,这意味着您需要一个 Main 的实例strong> 能够实例化 SingleProcessor。您的 SingleProcessor 实例将在后台有一个隐藏变量引用您的 Main 实例。就像你写这个一样:

        public class SingleProcessor implements Runnable {
        
            private Main main;
        
            public SingleProcessor(Main main){
                this.main = main;
            }
        
            @Override
            public void run() {
                System.out.println("single run starts" + Thread.currentThread().getName());
                Main.myStaticMethod();
                this.main.myInstanceMethod();
            }
        
        }
        

        并为您的所有 SingleProcessor 提供相同的 Main 实例。


        所有这些都是危险的,一个线程可能会在同时另一个线程正在修改它。有很多关于线程安全的高级主题。对于简单类型,您可以使用原子变量,参见 Concurrency package JavaDoc

        【讨论】:

        • 我不认为将静态变量与单例进行比较是一个好主意。静态变量用于单例的事实是这样做的一种方式。如果静态变量不是最终变量,则可以更改它。此外,我认为答案并不重要(例如讨论堆栈与堆)
        • 可能是对的,我不应该使用单例这个词。我试图制作一个“隐喻”,以表明在内存中,任何线程都将使用相同的引用。这可能会令人困惑。
        • 那不是真的。 java的内存模型不会对静态变量进行不同的处理。线程也将获得一份副本,并且无法保证最新更新的同步可见性
        • 不确定你在说什么......每个线程都会引用相同的内存位置,它引用另一个 single 内存位置(真正的 object 线程将要使用)。确保最新的可以及时更改,但是每个线程都会更改,除非他们明确保留对它的引用,否则他们不会拥有副本。我们假设我们谈论的是单个类加载器
        猜你喜欢
        • 1970-01-01
        • 2012-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多