2013-02-13 35 views
6

Tôi có một người bạn nói rằng tất cả các phương pháp tĩnh phải là synchronized trong ngữ cảnh của một ứng dụng web Java. Điều đó có đúng không? Tôi đã đọc nhiều trang tràn ngăn xếp khác về vấn đề này. Những gì tôi đã đi đến chỗ tin là bạn chỉ cần để đồng bộ hóa nếu bạn có:Java: Có phải tất cả các phương thức tĩnh cần được đồng bộ hóa không?

  1. Nhiều Chủ đề (Như trong một Sevlet container với một hồ bơi thread)
  2. ClassLoader Độc
  3. chung dữ liệu giữa các chủ đề, cho dù đó là dữ liệu Phiên hoặc dữ liệu thành viên thành viên.
  4. Dữ liệu được chia sẻ phải có thể thay đổi. Chỉ đọc dữ liệu là ok để chia sẻ.

Dựa trên điều này tôi nghĩ rằng các thành viên tĩnh phải được đồng bộ hóa, nhưng không phải là phương pháp tĩnh.

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

Mã này có chứng minh được điểm không?

CHỈNH SỬA: Đây chỉ là một số mã ném tôi đã tấn công để chứng minh điểm.

+8

Viết mã an toàn chủ đề ** phức tạp hơn nhiều so với tát 'đồng bộ hóa' ở những vị trí ngẫu nhiên. – SLaks

+1

Như một lưu ý phụ, 'safeStaticMethod' của bạn được viết sẽ vẫn an toàn bằng cách sử dụng một' StringBuffer', vì bộ đệm không được chia sẻ giữa các luồng. nó là cục bộ cho lời gọi phương thức cụ thể đó. – Charlie

+1

Để giải thích ở trên: về cơ bản không có quy tắc "nếu X sau đó Y" có giá trị chung để viết các chương trình an toàn. (Hầu hết những cái bạn nghe có thể kết thúc không cần thiết giảm đồng thời cho ứng dụng của bạn.) – millimoose

Trả lời

9

Không, không phải tất cả các phương pháp tĩnh cần được đồng bộ hóa. Danh sách của bạn về cơ bản là hoàn thành như xa như tôi có thể nhìn thấy. Hãy đặc biệt cẩn thận khi các phương pháp tĩnh hoặc

  1. truy cập một thành viên tĩnh có nghĩa là có thể thay đổi, hoặc
  2. được thông qua một tham chiếu đến một đối tượng có thể được sửa đổi.

Tôi nghĩ rằng không cần phải nói rằng 1 (có chủ đề ở nơi đầu tiên) là điều kiện tiên quyết, vì không có chủ đề synchronize không có ý nghĩa.

Tôi chưa bao giờ nghe 2, vì vậy tôi không biết chắc chắn đó có phải là sự cân nhắc hay không.

4

Điều đó không đúng và tôi chắc chắn điều đó sẽ gây bất lợi. Không phải mọi ứng dụng đều cần đồng thời và ngay cả trong các ứng dụng cần phải đồng thời, không phải mọi đoạn mã đều phải như vậy.

Bằng chứng khác, look at the source of String. Có nhiều phương pháp tĩnh trong đó, nhưng tôi chỉ có thể tìm thấy một phương thức được đồng bộ hóa và phương pháp đó thậm chí không phải là tĩnh.

+2

Theo như chuỗi không được đồng bộ, đó là vì String là bất biến - chủ yếu là vì các đối tượng bất biến là chỉ đọc và do đó _inh an toàn_ (điểm 4 trong câu hỏi ban đầu). Chuỗi thực sự là một trong những ví dụ tốt nhất của một lớp được thiết kế một cách rõ ràng để tránh việc sử dụng 'sync' và vẫn an toàn. – Matt

+2

Đúng vậy, ông đã chỉ ra rằng với điểm 4, nhưng câu hỏi là nếu ** tất cả ** phương pháp tĩnh cần được đồng bộ hóa và đây là ví dụ cho thấy, "không, họ không" –

+1

Ồ, quả thật vậy. Tôi hiểu bây giờ - ví dụ tốt :) – Matt

2

Phương pháp tĩnh gần như không bao giờ được đồng bộ hóa trong ứng dụng web. Trừ khi bạn chắc chắn 100% rằng những người duy nhất sẽ sử dụng ứng dụng là nhóm kế toán 3 người của bạn, và sẵn sàng đỏ mặt nếu nó mất toàn bộ công ty và tất cả những cú đột ngột dừng lại gần.

Tạo tài nguyên chung, chặn, được chia sẻ là tổng số lỗi trong khả năng mở rộng! Nó cũng sẽ gây ra cho bạn rất nhiều đau đầu và có khả năng khóa bạn vào một giải pháp phong cách Terracotta nếu bạn cuối cùng cần phải cụm máy chủ ứng dụng.

1

Trong một ứng dụng web (như một xây dựng bằng cách sử dụng servlet/JSP), bạn nên luôn luôn tránh đưa ra một phương pháp như đồng bộ vì nó thách thức toàn bộ triết lý của mutli sợi khả năng tiếp cận. Tại chỗ, luôn luôn cố gắng đặt mã cần thiết duy nhất, mà cần phải được truy cập từng cái một, bên trong khối đồng bộ.

1

Không hề. Chủ yếu là, các phương pháp tĩnh mà tôi đã gặp phải không sửa đổi bất kỳ biến tĩnh nào và do đó chúng không yêu cầu phải được đồng bộ.

Đối với sự hiểu biết đơn giản,

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

Phương pháp trên có thể được gọi bằng 1000s của đề nhưng nó sẽ là thread-an toàn vì phương pháp này chỉ đòi hỏi một argument- String name và đó là từ ngăn xếp chủ đề. Nó không được chia sẻ dữ liệu giữa các luồng.

Hãy suy nghĩ về điều đó, nếu tất cả các phương pháp tĩnh được đồng bộ hóa, các ứng dụng web sẽ rất chậm và lờ mờ để sử dụng. Chúng ta sẽ có khóa cấp lớp bất cứ khi nào một luồng đơn cố gắng truy cập phương thức.

Có rất nhiều phương pháp tĩnh trong các API do JDK cung cấp. Nếu tất cả đều được đồng bộ, tôi chắc chắn chúng tôi sẽ không sử dụng JAVA.

Trong trường hợp của bạn, có biến tĩnh (biến cấp lớp) đang được sửa đổi theo phương pháp tĩnh tĩnh. Có, nếu có nhiều luồng được tạo và chúng sẽ truy cập phương thức tĩnh, có khả năng là Sự can thiệp của luồng. Nó không an toàn chỉ vì có dữ liệu được chia sẻ giữa chúng.

Chủ yếu, các phương pháp tĩnh là các hàm tiện ích tùy thuộc vào các đối số được truyền cho chúng.

Xin lưu ý rằng phương pháp tĩnh không đồng bộ là chủ đề an toàn nếu chúng không sửa đổi biến lớp tĩnh.

Các vấn đề liên quan