Tap*_*ose 52 java oop static-methods utility thread-safety
是否存在任何通用方法或规则出口,通过这些方式或规则可以确保在任何应用程序的各种Utility类中专门使用的静态方法的线程安全性.在这里,我想特别指出Web应用程序的线程安全性.
众所周知,使用不可变对象作为参数的静态方法是线程安全的,而可变对象则不是.
如果我有一个实用程序方法进行某些操作java.util.Date并且该方法接受一个实例java.util.Date,那么这个方法就不是线程安全的.那么如何在不改变参数传递方式的情况下使其线程安全?
public class DateUtils {
public static Date getNormalizeDate(Date date) {
// some operations
}
}
Run Code Online (Sandbox Code Playgroud)
这类是javax.faces.context.FacesContext可变的吗?将此类的实例传递给此类静态实用程序方法是否安全?
这个类的列表,其实例可以作为参数传递或不能作为参数传递,可能很长; 那么在编写此类实用程序类的代码时,我们应该记住哪些要点?
Dun*_*nes 67
众所周知,具有不可变对象作为参数的静态方法是线程安全的,而可变对象则不是.
我会争辩这个.传递给方法的参数存储在堆栈上,这是一个每线程的习惯用法.
如果您的参数是一个可变对象,例如a,Date那么您需要确保其他线程不会在其他地方同时修改它.但这与您的方法的线程安全无关.
您发布的方法是线程安全的.它不维持任何状态,只对其参数进行操作.
我强烈建议你阅读Java Concurrency in Practice,或者一本专门讨论Java中线程安全的书.这是一个复杂的主题,无法通过一些StackOverflow答案正确解决.
ass*_*ias 16
由于您的类不包含任何成员变量,因此您的方法是无状态的(它只使用局部变量和参数),因此是线程安全的.
调用它的代码可能不是线程安全的,但这是另一个讨论.例如,Date不是线程安全的,如果调用代码读取由另一个线程写入的Date,则必须在Date编写和读取代码中使用正确的同步.
swa*_*ina 12
我看到很多答案,但没有一个真正指出原因。
所以这可以这么想,每当一个线程被创建时,它都是用自己的堆栈创建的(我猜创建时堆栈的大小是~2MB)。因此,发生的任何执行实际上都发生在该线程堆栈的上下文中。创建的任何变量都存在于堆中,但它的引用存在于堆栈中,但不存在于线程堆栈中的静态变量除外。
您所做的任何函数调用实际上都被推送到线程堆栈上,无论是静态的还是非静态的。由于完整的方法被压入堆栈,任何发生的变量创建都存在于堆栈中(同样例外是静态变量)并且只能被一个线程访问。
所以所有的方法都是线程安全的,直到它们改变某个静态变量的状态。
我建议在方法启动后立即创建该(可变)对象的副本,并使用副本而不是原始参数。
像这样的东西
public static Date getNormalizeDate(Date date) {
Date input = new Date(date.getTime());
// ...
}
Run Code Online (Sandbox Code Playgroud)
我们将通过一些例子来看看静态方法是否是线程安全的。
\n示例1:
\npublic static String concat (String st1, String str2) {\nreturn str1 + str2\n}\nRun Code Online (Sandbox Code Playgroud)\n现在上面的方法是线程安全的。
\n现在我们将看到另一个非线程安全的示例。
\n示例2:
\n public static void concat(StringBuilder result, StringBuilder sb, StringBuilder sb1) {\n result.append(sb);\n result.append(sb1);\n }\nRun Code Online (Sandbox Code Playgroud)\n如果您看到这两种方法都非常非常原始,但其中一种方法是线程安全的,另一种方法则不是。为什么?两者有什么区别?
\n实用程序中的静态方法是否容易出现非线程安全?很多\xe2\x80\x99s 的问题吧?
\n现在一切都取决于您如何实现方法以及您在方法中使用哪种类型的对象。您使用线程安全对象吗?这些对象/类是可变的吗?
\n如果您在示例 1 中看到 concat 方法的参数是 String 类型,它是不可变的并且按值传递,因此该方法是完全线程安全的。
\n现在,在示例 2 中,参数是 StringBuilder 类型,它是可变的,因此其他线程可以更改 StringBuilder 的值,这使得该方法可能是非线程安全的。
\n这又不完全正确。如果您使用局部变量调用此实用程序方法,那么您永远不会遇到任何与线程安全相关的问题。因为每个线程都使用它\xe2\x80\x99s自己的局部变量副本,所以你永远不会遇到任何线程安全问题。但这超出了上述静态方法的范围。它\xe2\x80\x99s取决于调用函数/程序。
\n现在实用程序类中的静态方法是一种常见的做法。那么我们怎样才能避免呢?如果您看到示例 2,我正在修改第一个参数。现在,如果您想让这个方法真正线程安全,那么您可以做一件简单的事情。要么使用不可变变量/对象,要么不更改/修改任何方法参数。
\n在示例 2 中,我们已经使用了可变的 StringBuilder,因此您可以更改实现以使静态方法线程安全,如下所示:
\npublic static String concat1(StringBuilder sb, StringBuilder sb1) {\nStringBuilder result = new StringBuilder();\nresult.append(sb);\nresult.append(sb1);\nreturn result.toString();\n}\nRun Code Online (Sandbox Code Playgroud)\n再次回到基础知识,永远记住,如果您使用不可变对象和局部变量,那么您就离线程安全问题很远了。
\n来自文章(https://nikhilsidhaye.wordpress.com/2016/07/29/is-static-method-in-util-class-threadsafe/)感谢Nikhil Sidhaye这篇简单的文章
\n我是这样想的:想象一个CampSite(这是一个静态方法)。作为一名露营者,我可以在帆布背包中带入一堆物品(这些参数在堆栈中传递)。CampSite为我提供了放置帐篷和露营炉等的地方,但是如果CampSite唯一要做的就是允许我修改自己的对象,则它是线程安全的。CampSite甚至可以凭空(FirePit firepit = new FirePit();)创建事物,这些事物也可以在堆栈上创建。
任何时候,我都可以将所有物品放在我的行李箱中消失,而其他露营者中的任何一个都可以出现,完全按照他们上次消失时的状态进行。此CampSite中的不同线程将无法访问在其他线程中创建的CampSite堆栈上的对象。
假设只有一个campStove(CampStove的单个对象,而不是单独的实例化)。如果通过某种想象力我共享一个CampStove对象,则需要考虑多线程问题。我不想打开我的campStove,消失,然后在其他露营者将其关闭后重新出现-我将永远在检查我的热狗是否煮过,永远不会。您必须在CampStove类中,在调用CampSite的方法中或在CampSite本身中进行一些同步,但是就像Duncan Jones所说的那样,“那是另一回事”。
请注意,即使我们驻留在非静态CampSite对象的单独实例中,共享campStove也会具有相同的多线程注意事项。