单例方法

Singletone method

我需要一种始终在一个实例中执行的方法。例如,此方法同时从不同的 class 个实例和不同的线程调用。在这种情况下,它们应该一次执行一个,而不是同时执行,尽管它们是从不同的实例调用的。 我不能让它只是静态的和同步的,因为它有很多内部代码,不能重构为静态的 现在我有了下一个想法:

private static Boolean isMethodRun = false;

public void singletoneMethod() {
        synchronized(Boolean) {
            if (!isMethodRun) {
            isMethodRun = true;
            } else {
                for (int i = 0; i < 10; i++) {
                    if (isMethodRun) {
                        Thread.sleep(2_000);
                    } else {
                        isMethodRun = true;
                        break;
                    }
                }
            }
        }
        try {
            //inner code
        } catch (Throwable e) {
            //logs
            throw e;
        } finally {
            isMethodRun = false;
        }
    }

有没有更漂亮的方法来获得这样的功能

所以基本上你一次只需要一个 singletonMethod 调用1.

这是最简单的解决方案:

public static synchronized void singletonMethod() {
    // your business logic here 
}

你说:

I can't make it just static and synchronized, because it have a lot of inner code, which can't be refactored to static

我不相信它不能被重构2,但如果你这么说,这里有一些替代方案:

private static final Object lock = new Object();  // Must be static!

public void singletonMethod() {
    synchronized (lock) {
        // your business logic here
    }
}

public static synchronized void oneAtATime(Runnable runnable) {
     runnable.run(); 
}

您可以像这样使用 oneAtATime 方法:

oneAtATime(() -> { // your business logic here });  // Java 8+

oneAtATime(new Runnable() {
               public void run() {
                   // your business logic here
               }
});

除了第一个(在顶部)之外,这些替代方案都允许您在“业务逻辑”中引用实例变量。您只需要在适当的范围内实现“业务逻辑”。 (即使在第一种情况下,将实例引用传递给静态方法以便它可以调用实例上的方法也是微不足道的。)

要以线程安全的方式使用上述内容,您需要确保“业务逻辑”都是线程安全的。这将取决于应用程序中的其他代码;例如其他线程/方法如何使用此“业务逻辑”运行的任何共享对象。


why would static be needed here? Can't it just be public synchronized void singletonMethod? Or do the memory barriers not work for accessing static fields? – luk2302 25 mins ago

我们需要一个 static 锁或一个 static synchronized 方法来满足一次只能进行一个调用的规定要求。如果有多个锁,则不会得到序列化。

可以将“一次一个”逻辑实现为(正确实现的)单例 class 的 synchronized 实例方法。但是,除非您对 Singleton 有其他好的用途,否则没有太大意义 class。 (请记住,单例模式会带来其自身的问题。)


1 - 这不是“单例”一词通常的意思...
2 - 事实上,所需的重构是微不足道的。只需将您的业务逻辑放入声明为(比如)businessLogic()YourClass 的实例方法中。那么你可以在YourClass中定义一次一个方法如下:static synchronized void singleton(YourClass arg) { arg.businessLogic(); }。您通常会像这样使用它(在 YourClass 内):YourClass.singleton(this);

最简单的方法是同步方法。由于您似乎已将 isMethodRun 变量定义为静态,因此您似乎希望在静态事物上进行同步:

private static final Object lock = new Object();

public void singletoneMethod() {
  synchronized (lock) {
    // The code you want only to execute one-at-a-time.
  }
}

您可以在 class - synchronized (WhateverYourClass.class) 上同步 - 但 JVM 中的任何人在其他地方 class 上同步时都容易受到攻击。像这样使用不可访问的 Object 更健壮。