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

时间:2011-06-01 14:37:06

标签: java multithreading static static-methods

我在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的块?如果我应该使用一个块,我应该使用什么对象来锁定它?

7 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:5)

  

我可以将这些计算用于静态辅助函数吗?如果服务器有足够的处理器内核,那么对该静态函数的多次调用(由多个不同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();
        }
    }
}

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

答案 2 :(得分:4)

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

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();
}

}

答案 3 :(得分:2)

我将在这里总结一下Matt Ball的回答中的评论,因为它最后很长时间并且消息丢失了:消息是

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

但是突然之间所有用户可能会尝试在上午9点完成计算,你的应用程序将停止工作!我的意思是并没有真正停止,但他们都会阻止锁定并排起一大队。

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

会话管理也应该更好地留给容器:它已经过优化,可以有效地处理它们,如果有必要,包括群集等等。我怀疑如果不投入大量工作并在那里制造大量错误,可以做出更好的解决方案。但正如Matt Ball所说,应该更好地单独询问。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

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

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