2010-09-28 47 views
3

说我有两个类是这样的:优势在Java中使用静态变量的

class A{ 
    private static Random random = new Random(); 

    public A(){ 
     // Do something. 
    } 

    public Integer methodGetsCalledQuiteOften(){ 
     return random.nextInt(); 
    } 
} 

class B{ 
    private Random random; 

    public A(){ 
     random = new Random(); 
     // Do something. 
    } 

    public Integer methodGetsCalledQuiteOften(){ 
     return random.nextInt(); 
    } 
} 

在这样一个场景,他们都被实例化多次和这两个类实例方法的methodGetsCalledQuiteOften被调用了很多,是否有任何real使用静态变量的优势/劣势(时间,内存)在类A中保存Random()而不是在每个实例中创建一个新的Random()对象,就像在类B中一样?

该应用程序是多线程和更高级别的随机性,所以我认为我会与静态SecureRandom。如果这将是一个真正的速度因素分析后,我可能会选择别的东西。

回答

6

real优点/缺点取决于real code。换句话说,它取决于创建B的频率与调用方法的频率等相关的频率。您应该对应用程序进行配置并查看它是否有合理的区别。

A会比B更高效吗?当然。但是否会注意取决于您的使用情况。 A会使用比B更少的内存吗?当然,但不管你是否在意取决于你有多少A/B的实例。

真的唯一的其他考虑因素是决定论。既然你没有为Random实例指定种子,我认为你不关心你是否可以从Random中重现数字序列。但是值得注意的是,如果你有一个共享的随机数,那么对于某些A实例确保某个确定性的数字序列要比对B中的每个实例确定一个确定性的序列要困难得多。

+0

静态实例始终运行。是否有必要在调用新类时停止随机静态实例? – 2013-05-07 05:13:48

1

访问静态资源不是线程安全的。在线程环境中,您可能会得到奇怪/不可预知的结果。

+1

随机类确保内部同步:您可以检查来源。 – 2010-09-28 21:01:56

+0

*不同步*访问静态资源*可能*不是线程安全的。这取决于资源。 – 2010-09-28 21:02:42

+0

对实例字段的访问本质上也不是线程安全的,并且在线程环境中可能会出现奇怪/不可预知的结果。 – 2010-09-28 21:02:47

2

当实例不希望发生更改或想要在实例之间共享实例时使用静态变量。

private final static int SOME_CONSTANT=1; 

就是一个例子。

public class USA { 
    private final static Map statesOfTheUnion = new HashMap(); 
    // etc 
} 

可能是另一个。在后一种情况下,我们并不认为各州会因实例而异。因此对于每个实例都有自己的副本是没有意义的。

+2

你是不是故意把'final'和'Collections.unmodifiableMap'放在那里? – 2010-09-28 21:21:48

+0

好主意! ;-)这只是一个例子。 :-)我离开'final'更像是OP的代码。但是,我不记得曾经使用'static'而没有'final'。 – 2010-09-28 22:21:46

+0

我加了'final's。好的观察。 – 2010-09-28 22:26:23

1

那么,因为Random类采取了必要的步骤来确保线程安全性,所以我没有发现将它变为静态的问题。

而且由于其状态包括单AtomicLong对象(8个字节),它是内存中没有浪费大(除非你打算创建巨大B实例)。

所以,我会说,这两种方法没有什么区别。

+0

如果他们把它放在'Random'的文档中,而不是依靠代码检查,那会很好。 – 2010-09-28 21:02:18

+1

文档确实说'Random'是线程安全的,只是不清楚:“接下来的方法是通过类Random实现的,通过原子更新种子来实现......” – erickson 2010-09-28 21:04:46

+0

@Mark好的一点,Java中很少有标准类这个问题。 – 2010-09-28 21:04:51

3

如果你的程序是单线程的,你的Random应该是一个类成员(static)。这是更高效一点。使用SecureRandom这一点甚至更为重要,这可能需要相当长的时间才能开始播种。

如果多个线程最终会调用methodGetsCalledQuiteOften(),则问题会更复杂一点。该类是线程安全的,但在并发更改期间保护其状态所需的开销可能与创建新的独立实例Random相当。

我可能会坚持一个static成员,直到我注意到随机数发生器线程之间的争用很多,这可能永远不会发生。

+0

第一个'SecureRandom'可能会加载一个安全基础架构并且需要很长时间。然而,在那之后它应该比较快速,尽管不如旧的可预测的java.util.Random快。 – 2010-09-28 21:19:58

0

就时间和内存而言,我想不出任何大的优势(静态变量将成为类定义的一部分而不是堆)。但是,当您知道将从多个位置访问对象时,静态变量很有用。 hvgotcodes是正确的,但使用静态资源不是线程安全的。但是,如果你只读静态值,然后使用线程是好的。

这将不必通过才能使用random

1

到发起对象(A或B)到其他对象引用如果您正在使用java.util.Random救你,请注意,这是一个linear congruential generator众所周知的与相关性有关的问题。对我而言,真正的问题是应用程序是应该从一个系列中选择,还是从具有不同种子的系列中选择。

2

虽然实例化A类比B类实质化速度快(10倍),但只有当您每秒处理100.000次时才重要。

关于线程安全的,这是我的理解是.nextInt()基于的.next()这应该是线程安全的。所以多线程不应该在这里给出任何问题。

但更重要的是,这两个类的行为可能会有所不同。类A的实例将给每个数字稍微随机的序列,但是类B的实例可能会给出相同的“随机”数字序列(这种行为在不同的平台上可能不同)。所述Javadoc规范规定:

如果随机的两个实例被创建 使用相同的种子,和方法相同的 调用序列对每个 制成,它们将生成并返回号码 相同的序列。

因此,如果随机性是非常重要的,你可能要考虑给人一种独特的种子随机构造函数或更好地与慢java.security.SecureRandom中实现随机的。

5

只是说不可变的静态,m'kay。

这是一个残酷的设计。通常引用的结果是可测试性失败。如果不重新加载课程,将很难重现。糟糕的设计对于许多其他原因也不好。只要提一个,默认情况下,类应该是线程无关的。添加一个可变的静态,你是线程敌对的 - 一个糟糕的地方。

每次创建一个新实例看起来很浪费。当然,尽管不会过早优化(如果您正在进行优化,则不会使用java.util.Random)。最初看到递增也可能导致问题。

最好的方法是传入一个实例(“Parameterise from Above”或“正确使用构造函数”)。然后你可以测试,复制,模拟等。你是高效的,可以在不同的实现中翻转。静态变量的

+0

+1 yep yep。 _添加一个可变静态,你是线程敌对_ – 2010-09-28 22:25:18

-1

利益(主要分):

  1. 常量可以不采取附加的存储器(一个用于每个类)来定义。

  2. 可以在没有实例化类的情况下访问常量。

优势的静态方法:

实例独立的行为,而不必担心意外的互动与类的实例来定义。