8

我有一个朋友说所有静态方法都应该synchronized在 Java Web 应用程序的上下文中。真的吗?我已经阅读了许多其他关于此的堆栈溢出页面。我开始相信,如果您有以下情况,您只需要同步:

  1. 多线程(如在具有线程池的 Sevlet 容器中)
  2. 单个类加载器
  3. 线程间共享数据,无论是Session数据还是静态成员数据。
  4. 共享数据必须是可变的。只读数据可以共享。

基于此,我认为静态成员应该是同步的,而不是静态方法。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadTest {

    static String staticString = "";

    // This static method is safe b/c it only uses local data.
    // It does not use any shared mutable data.
    // It even uses a string builder.
    static String safeStaticMethod(String in) {
        // This also proves that StringBuilder is safe
        // When used locally by a thread.
        StringBuilder sb = new StringBuilder();
        sb.append("Hello: ");
        sb.append(in);
        return sb.toString();
    }

    // This static method is not safe b/c it updates and reads
    // shared mutable data among threads.
    // Adding synchronized will make this safe.
    static String unsafeStaticMethod(String in) {
        staticString = in;
        StringBuffer sb = new StringBuffer();
        sb.append("Hello: ");
        sb.append(staticString);
        return sb.toString();
    }

    public static void main(String[] args) {
        ThreadTest test = new ThreadTest();
        test.staticMethodWithLocalData();
        test.staticMethodWithStaticData();
    }

    public void staticMethodWithLocalData() {

        ExecutorService executor = Executors.newFixedThreadPool(2);
        final int iterations = 100000;

        executor.submit(new Runnable() {

            @Override
            public void run() {
                for (int index = 0; index < iterations; ++index) {
                    if (!safeStaticMethod("Thread1").equals("Hello: Thread1")) {
                        System.out.println("safeStaticMethod at " + index);
                    }
                }
            }
        });

        executor.submit(new Runnable() {

            @Override
            public void run() {
                for (int index = 0; index < iterations; ++index) {
                    if (!safeStaticMethod("Thread2").equals("Hello: Thread2")) {
                        System.out.println("safeStaticMethod at " + index);
                    }
                }
            }
        });
    }

    public void staticMethodWithStaticData() {

        ExecutorService executor = Executors.newFixedThreadPool(2);
        final int iterations = 100000;

        executor.submit(new Runnable() {

            @Override
            public void run() {
                for (int index = 0; index < iterations; ++index) {
                    if (!unsafeStaticMethod("Thread1").equals("Hello: Thread1")) {
                        System.out.println("unsafeStaticMethod at " + index);
                    }
                }
            }
        });

        executor.submit(new Runnable() {

            @Override
            public void run() {
                for (int index = 0; index < iterations; ++index) {
                    if (!unsafeStaticMethod("Thread2").equals("Hello: Thread2")) {
                        System.out.println("unsafeStaticMethod at " + index);
                    }
                }
            }
        });
    }
}

这段代码是否证明了这一点?

编辑:这只是我为了证明这一点而编写的一些一次性代码。

4

5 回答 5

12

不,并非所有静态方法都需要同步。据我所知,你的清单基本上是完整的。使用静态方法时要特别小心

  1. 访问一个可变的静态成员,或者
  2. 获得对可以修改的对象的引用。

我认为不用说 1(首先有线程)是一个先决条件,因为没有线程synchronize是没有意义的。

我从来没有听说过2,所以我不确定这是否是一个考虑因素。

于 2013-02-13T20:32:00.943 回答
4

不,那不是真的,我相信这将是有害的。并非每个应用程序都需要并发,即使在确实需要并发的应用程序中,也不是每段代码都必须是并发的。

作为更多证据,请查看 String 的来源。 那里有很多静态方法,但我只能找到一种同步方法,而且那个甚至不是静态的。

于 2013-02-13T20:34:21.617 回答
2

静态方法几乎应该在 webapp 中同步。除非您 100% 确定唯一会使用该应用程序的人是您的 3 人会计团队,并且如果它在整个公司范围内起飞并且突然间几乎停顿,他们愿意脸红。

创建一个全局的、阻塞的、共享的资源是可伸缩性的彻底失败!如果您最终需要集群应用程序服务器,它也会给您带来很多麻烦,并且可能会将您锁定在 Terracotta 风格的解决方案中。

于 2013-02-13T20:53:58.540 回答
1

在 Web 应用程序中(例如使用 servlet/JSP 构建),您应该始终避免使方法同步,因为它挑战了多线程可访问性的整个理念。在适当的位置,始终尝试将唯一必要的代码(需要逐个访问)放置在同步块中。

于 2014-05-14T00:56:56.840 回答
1

一点也不。大多数情况下,我遇到的静态方法不会修改任何静态变量,因此它们不需要同步

为了简单理解,

    //sample static util method to get string in upper case    
    public static String getName(String name){
        return a.toUpperCase();
    }

上面的方法可以被 1000 多个线程调用,但是它将是线程安全的,因为该方法只需要一个参数 -字符串名称,并且来自线程堆栈。它不是线程之间的共享数据。

想一想,如果所有的静态方法都同步,那么 Web 应用程序使用起来会非常缓慢和昏昏欲睡。每当单个线程尝试访问该方法时,我们将拥有类级别的锁。

JDK提供的API中有很多静态方法。如果所有这些都是同步的,我很确定我们不会使用 JAVA。

在您的情况下,静态方法正在修改一个静态变量(类级别变量) 。是的,如果创建了多个线程并且它们要访问静态方法,则可能存在线程干扰。它不是线程安全的,因为它们之间存在共享数据

大多数情况下,静态方法是实用函数,具体取决于传递给它们的参数。

请注意,如果不修改静态类变量,非同步静态方法是线程安全的

于 2017-02-01T09:57:37.043 回答