3

我有一个 Java 中的 Singelton(在 OSGi 服务中),并希望在其中维护一些状态(计数器)。

这个变量应该是静态的吗?或同步?或两者?

或者我应该将动作包装在同步方法中?(这与仅使 var 同步有什么不同吗?)

我希望服务操作的消费者增加这个计数器。

public MyServiceImpl implements MyService {
    private int count = 0; // static? syncronized?

    public String helloWorld() { count++; return "Hello World"; }
    public int getHelloCount() { return count; }
}

更新:我怎么会像地图或列表这样的东西?是否也更喜欢使用这些的原子版本?或者同步更好?

4

3 回答 3

7

单例的问题是它们需要一个范围。如果您在 OSGi 中注册服务,那么这是框架中的单例。然而,由于 OSGi 避免了像瘟疫这样的静态问题,人们可以在同一个 VM 中启动多个框架(嵌套或作为兄弟),这可能意味着您的服务在不同的框架中被多次注册。一般来说,这正是您想要的。如果这还不够单例,那么范围应该是什么?虚拟机、进程、机器、网络、世界?人们为您提供的创建单例的所有技巧都忘记告诉您它们的范围仅适用于您碰巧所在的类加载器。

在 OSGi 中,假设您的范围是框架。所以只需注册一个服务并使用实例变量。由于 OSGi 在并发环境中运行,因此正如所有其他帖子所指出的那样,您必须使用同步方法或更好的 AtomicLong/AtomicInteger。

如果您有多个服务需要共享一个单例,只需创建一个额外的服务来表示单例。

永远不要使用静态变量,因为它们会显着降低代码的可重用性,它们具有全局变量的所有弊端。纯 OSGi 的优点之一是它允许您几乎完全使用实例进行编程,而不必使用静态和类名(它们遭受相同的全局变量问题)。

于 2013-04-18T07:02:05.827 回答
2

这是使用 Atomics 的绝佳机会:

public class MyServiceImpl {
  private AtomicInteger helloCount = new AtomicInteger(0);

  public String helloWorld() {
    helloCount.incrementAndGet();
    return "Hello World";
  }

  public int getHelloCount() {
    return helloCount.get();
  }
}

这是无锁的,因此通常更快、更有效。

这种机制同样适用于单例或非单例。

于 2013-04-17T22:58:02.643 回答
2

如果您只注册一次普通的 OSGi 服务,那么它就是该框架中的单例。 如果您使用的是声明式服务,那么默认情况下会发生这种情况。

正常我的意思是不是由服务工厂创建的。框架位也很重要,一些供应商在单个 JVM 中支持多个框架。

不要使用静态 - 这不是OSGi 方式:-)

您关心的只是并发访问,因此正如其他人所评论的那样,类似的东西AtomicLong是合适的。

注意溢出 - 你希望你的计数器达到Long.MAX_VALUE(> 9,223,372,036,854,775,807)吗?否则你最终会看到负数。

于 2013-04-18T00:45:10.323 回答