2011-06-01 28 views
9

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

+0

谢谢大家,你绝对回答了我的问题,并且给了我思考的食物。不幸的是,我不能接受多个答案: -/ – MarioP 2011-06-01 19:38:56

回答

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

理想的情况是,你写的辅助类是不可变的(无状态或其他方式),这样你不担心线程安全。

+0

在未知负载的Web应用程序环境中,静态锁定同步可能会导致服务器性能问题 - 这取决于花费在同步代码上的负载和时间。我见过这样的“解决方案”,导致整个应用程序服务器无法使用:有大量的请求等待服务等待这个瓶颈。 – 2011-06-01 14:46:53

+0

因此我的评论在最后。 – 2011-06-01 14:56:06

+0

有一些需要共享的对象,例如会话管理。这将是一个优雅的解决方案吗? – MarioP 2011-06-01 15:01:02

5

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

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

+1

第一个用例不需要是线程安全的,因为只有原始参数并且不涉及类成员。还是我想念那里的东西? – MarioP 2011-06-01 14:42:56

+0

@marioP,多数民众赞成在正确的,但总的来说,这可以用没有太多困难的情况下完成,所以我的答案是从静态方法一起走开。它不是很好恕我直言,如果一半的计算是静态方法,另一半是在实例... – hvgotcodes 2011-06-01 14:44:48

+0

@Mario如果没有状态存储在类字段('静态'或非静态),那么你很好。 – 2011-06-01 14:45:50

1

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

+1

我不同意。拥有良好的静态效用方法非常普遍,也是一件好事。它减少了复制和粘贴代码,并保持“干净”和简单,因为不那么复杂。 – 2011-06-01 14:56:07

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

} 
+0

不幸的是,它必须被共享,但我会看看ThreadLocal类。也许它在其他一些情况下证明是有用的。 – MarioP 2011-06-01 15:27:39

1

在第一种情况下,您不必担心线程问题,因为变量对于每个线程都是本地的。不过,您可以在第二种情况下正确识别问题,因为多个线程将读取/写入同一个对象。同步方法将会工作,同步块也一样。

1

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

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

2

我会在这里说了些什么总结的意见对马特·鲍尔的回答,因为它有很长末及信息丢失:该消息是

在一个共享的环境,比如一个Web /应用程序服务器,您应该尽力找到没有同步的解决方案。在一个多用户/多应用场景下,使用在静态对象上同步的静态帮助器可能对单独应用程序的单独应用程序来说足够好,这样做很可能以非常差的性能结束 - 这实际上意味着将访问序列化您的应用程序,所有用户将不得不在同一个锁上等待。您可能很长时间没有注意到问题:如果计算速度够快并且负载均匀分布。

但是突然间所有用户都可能会尝试在上午9点进行计算,并且您的应用程序将停止工作!我的意思不是真的停止,但他们都会阻止锁定,并形成一个巨大的队列。

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

会话管理也应更好地留给容器:它已被优化,有效地处理它们,如有需要,包括集群等我怀疑人们可以做出更好的解决方案,而在途中投入大量的工作,赚很多的bug那里。但正如马特·鲍尔所说,应该更好地单独询问。