Java:是否需要同步所有静态方法?

时间:2013-02-13 20:30:02

标签: java servlets synchronization thread-safety

我有一位朋友说在Java Web应用程序的上下文中所有静态方法都应该是synchronized。真的吗?我已经阅读了很多关于此的堆栈溢出页面。我所相信的是,如果你有以下内容,你只需要同步:

  1. 多个线程(如在带有线程池的Sevlet容器中)
  2. Single ClassLoader
  3. 线程之间的共享数据,无论是会话数据还是静态成员数据。
  4. 共享数据必须是可变的。只读数据可以分享。
  5. 基于此,我认为静态成员应该是同步的,而不是静态方法。

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

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

    编辑:这只是我破解的一些一次性代码来证明这一点。

5 个答案:

答案 0 :(得分:10)

不,并非所有静态方法都需要同步。就我所见,你的清单基本上已经完成。静态方法时要特别小心

  1. 访问可变的静态成员,或
  2. 传递对可以修改的对象的引用。
  3. 我认为不用说1(首先有线程)是一个先决条件,因为没有线程synchronize没有意义。

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

答案 1 :(得分:4)

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

作为更多证据,look at the source of String.那里有很多静态方法,但我只能找到一个同步方法,而且一个方法甚至不是静态方法。

答案 2 :(得分:2)

静态方法几乎应该从不在webapp中同步。除非你100%确定将会使用该应用程序的唯一人员是你的3人会计团队,并且如果它在全公司范围内起飞并且所有的突然研究几乎停止,那么他们愿意变红。

创建全局,阻塞,共享资源是可扩展性的彻底失败!如果您最终需要集群应用程序服务器,这也会让您感到很头疼,并可能将您锁定为Terracotta风格的解决方案。

答案 3 :(得分:1)

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

答案 4 :(得分: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。

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

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

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