3

我遇到了一个这样设置的类:

public class MyClass {

  private static boolean started = false;

  private MyClass(){
  }

  public static void doSomething(){
    if(started){
      return;
    }
    started = true;
    //code below that is only supposed to run
    //run if not started
  }
}

我对静态方法的理解是,除非它们是常量并且不会更改,否则不应在其中使用类变量。相反,您应该使用参数。我的问题是为什么通过执行 MyClass.doSomething() 多次调用时这不会中断。在我看来,它不应该起作用,但确实起作用。它只会通过 if 语句一次。

那么有人可以向我解释为什么这不会中断吗?

4

9 回答 9

11

方法doSomething()和变量started都是静态的,所以变量只有一个副本,可以从doSomething(). 第一次doSomething()被调用,started是假的,所以它设置started为真,然后……嗯,某事。它被调用的第二次和随后的时间started是 true,所以它不做任何事情就返回。

于 2009-03-03T12:29:11.660 回答
6

没有理由使用静态变量不起作用。我并不是说这是特别好的做法,但它会起作用。

应该发生的是:

  1. 第一个电话被打了。类已初始化,started 为假。
  2. doSomething 被调用。if 失败并且代码绕过它。started 设置为 true 并且其他代码运行。
  3. 再次调用 doSomething。if 通过并且执行停止。

需要注意的一件事是这里没有同步,因此如果在非常接近的单独线程上调用 doSomething(),每个线程都可以读取为 false,绕过 if 语句并执行工作,即存在竞争条件。

于 2009-03-03T12:33:29.263 回答
6

给出的代码不是线程安全的。使此代码线程安全的简单方法是执行类似的操作

public class MyClass {

  private static AtomicBoolean started = new AtomicBoolean(false);

  private MyClass(){
  }

  public static void doSomething(){
    boolean oldValue = started.getAndSet(true);
    if (oldValue)
      return;
    }

    //code below that is only supposed to run
    //run if not started
  }
}

这应该是线程安全的,因为 AtomicBoolean getAndSet 是同步的。

诚然,如果您不使用线程,这不是问题(请注意,Web 应用程序可以使用相当多的线程来处理各种请求,而您却没有意识到这一点)。

于 2009-03-03T12:55:55.933 回答
2

这不是特别好的代码——通常设计应该使用状态改变的对象实例,但它没有任何违法之处。

我对静态方法的理解是,除非它们是常量并且不会更改,否则不应在其中使用类变量。

您似乎已经从设计指南推断为语言功能。阅读在线提供的众多 Java 教程之一,了解该语言中实际允许的内容。您可以在静态方法中自由使用非最终静态字段,但这会导致程序化而不是面向对象的代码。

相反,您应该使用参数。

很难看出如何使用started参数——如果调用者知道进程已经启动,他们为什么要调用该方法?

于 2009-03-03T12:33:54.473 回答
1

在静态方法中,您可以调用或访问同一类中的静态成员。

忽略多线程场景,第一次调用 doSomething 将使布尔静态变量为 true,因此,第二次调用将执行 if 块的代码,只是简单地退出该方法。

于 2009-03-03T12:30:36.260 回答
1

您的静态方法正在与静态类变量对话,所以应该没问题。您可以将其视为全局代码和全局变量,但它位于类的命名空间中。

如果您尝试访问非静态成员变量:

private int foo = 0;

在静态方法中,编译器会并且应该抱怨。

started is false - initial state.
MyClass.doSomething() - statered is now true
MyClass.doSomething() - started is STILL true

MyClass foo = new MyClass();
foo.started -> it's STILL true, because it's static
foo.doSomething() - not sure you can do this in Java, but if you can, it's be STILL TRUE!

现在,上面的代码中存在线程安全问题,但除此之外,它似乎按设计工作。

于 2009-03-03T12:37:02.797 回答
1

只要记住“静态变量是类级变量,所有非静态变量都是实例变量”的经验法则。那么你就不会有任何困惑了!

即对于静态变量,代码中对变量的所有引用都指向相同的内存位置。对于非静态变量,每当创建该类的新实例时都会完成新的内存分配(因此代码中对变量的每个引用都指向为调用类实例分配的不同内存位置)。

于 2009-03-03T12:57:48.957 回答
0

上面的代码运行良好(除非它在多线程环境中运行)。为什么你认为它应该打破?

于 2009-03-03T12:32:34.307 回答
0

我对静态方法的理解是,除非它们是常量,否则不应在其中使用类变量,并且不要更改

我猜只能访问静态成员。它不必是恒定的!

我的问题是为什么通过执行 MyClass.doSomething() 多次调用时这不会中断。在我看来,它不应该起作用,但确实起作用。它只会通过 if 语句一次

按照现有的逻辑。只有第一个调用运行该//code to be run部分

于 2009-03-03T12:33:03.430 回答