调用静态帮助程序方法的多个线程



我有一个在Tomcat上运行的Web应用程序。

需要在 Web 应用程序中的多个位置执行多个计算。我可以将这些计算设为静态帮助程序函数吗?如果服务器有足够的处理器内核,对该静态函数的多次调用(由对不同 servlet 的多个请求导致(是否可以并行运行?还是一个请求必须等到另一个请求完成调用?

public class Helper {
    public static void doSomething(int arg1, int arg2) {
        // do something with the args
        return val;
    }
}

如果调用并行运行:我还有另一个带有静态函数的辅助类,但该类包含一个在静态函数中使用的私有静态成员。如何确保函数是线程安全的?

public class Helper {
    private static SomeObject obj;
    public static void changeMember() {
        Helper.obj.changeValue();
    }
    public static String readMember() {
        Helper.obj.readValue();
    }
}

changeValue()readValue()读取/更改 Helper.obj 的相同成员变量。我是否必须使整个静态函数同步,或者只是使用Helper.obj块?如果我应该使用块,我应该使用什么对象来锁定它?

我能否使这些计算成为静态帮助程序函数? 如果服务器有足够的处理器内核,对该静态函数的多次调用(由对不同 Servlet 的多个请求导致(是否可以并行运行?

的,是的。

我是否必须使整个静态函数同步

那会起作用。

或者只是使用Helper.obj的块

那也将奏效。

如果我应该使用

块,我应该使用什么对象来锁定它?

使用static Object

public class Helper {
    private static SomeObject obj;
    private static final Object mutex = new Object();
    public static void changeMember() {
        synchronized (mutex) {
            obj.changeValue();
        }
    }
    public static String readMember() {
        synchronized (mutex) {
            obj.readValue();
        }
    }
}

不过,理想情况下,您将帮助程序类编写为不可变(无状态或其他(,这样您就不必担心线程安全性。

您应该捕获类中的计算,并为每个线程创建该类的实例。 如您所知,您现在拥有的不是线程安全的,并且要使其线程安全,您必须在静态资源/访问该静态资源的方法上进行同步,这将导致阻塞。

请注意,有一些模式可以帮助您解决此问题。 您可以使用策略模式(在其规范形式中,必须在运行时选择策略,这可能适用于也可能不适用于此处(或变体。 只需使用执行方法(以及具有该方法的接口(为每个计算创建一个类,并传递要执行的上下文对象。 上下文保存计算的所有状态。 每个线程一个策略实例及其上下文,您应该没有任何问题。

如果您不必共享它,则可以将其设置为线程本地,那么它不必是线程安全的。

public class Helper {
private static final ThreadLocal<SomeObject> obj = new ThreadLocal<SomeObject>() {
    public SomeObject initialValue() {
        return enw SomeObject();
    }
}
public static void changeMember() {
    Helper.obj.get().changeValue();
}
public static String readMember() {
    Helper.obj.get().readValue();
}
}

我将在这里总结一下马特·鲍尔(Matt Ball(答案的评论中所说的内容,因为它在最后变得很长并且消息丢失了:消息是

在像 Web/应用程序服务器这样的共享环境中,您应该非常努力地在不同步的情况下找到解决方案。使用在静态对象上同步的静态帮助程序可能足以适用于屏幕前只有一个用户的独立应用程序,在多用户/多应用程序方案中,这样做很可能会以非常差的性能结束 - 这实际上意味着序列化对应用程序的访问,所有用户都必须等待同一个锁。您可能在很长一段时间内都没有注意到这个问题:如果计算足够快并且负载均匀分布。

但是突然之间,您的

所有用户可能会尝试在上午 9 点进行计算,您的应用程序将停止工作!我的意思是不是真的停下来,但他们都会堵在锁上并排起长队。

现在,不管共享状态的必要性如何,既然您最初将计算命名为同步主题:它们的结果是否需要共享?或者这些计算特定于用户/会话?在后一种情况下,根据Peter Lawrey的说法,ThreadLocal就足够了。否则,我会说为了整体性能,最好为需要它们的每个人复制计算,以便不同步(取决于成本(。

会话管理也应该更好地留给容器:它已经过优化以有效地处理它们,如有必要,包括集群等。我怀疑一个人可以在不投入大量工作并在途中制造大量错误的情况下做出更好的解决方案。但正如马特·鲍尔(Matt Ball(所说,最好单独询问。

在第一种情况下,您不必担心线程问题,因为变量是每个线程的局部变量。 但是,在第二种情况下,您可以正确识别问题,因为多个线程将读取/写入同一对象。 同步方法将起作用,同步块也是如此。

对于第一部分:是的,这些调用是独立的,并且在由不同线程调用时并行运行。

对于最后一部分:在并发对象、虚拟对象或类对象上使用同步块。请注意级联同步块。当以不同的顺序获取时,它们可能会导致死锁。

如果您担心同步和线程安全,请不要使用静态帮助程序。使用帮助程序方法创建一个普通类,并根据 servlet 请求创建一个实例。保持简单:-(

最新更新