2013-02-13 67 views
6

मेरे पास एक दोस्त है जिसने कहा है कि जावा वेब एप्लिकेशन के संदर्भ में सभी स्थैतिक विधियां synchronized होनी चाहिए। क्या यह सच है? मैंने इसके बारे में कई अन्य स्टैक ओवरफ्लो पेज पढ़े हैं।जावा: क्या सभी स्थिर तरीकों को सिंक्रनाइज़ करने की आवश्यकता है?

  1. एकाधिक धागे (एक Sevlet कंटेनर में के रूप में एक धागा पूल के साथ)
  2. धागे के बीच साझा किया गया डेटा, चाहे
  3. एकल classloader: क्या मेरा मानना ​​है कि के लिए आए हैं कि आप केवल सिंक्रनाइज़ करने के लिए यदि आपके पास की जरूरत है यह सत्र डेटा या स्थिर सदस्य डेटा है।
  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); 
        } 
       } 
      } 
     }); 
    } 
} 

क्या यह कोड बिंदु साबित करता है?

संपादित करें: यह केवल कुछ फेंकने वाला कोड है जो मैंने बिंदु साबित करने के लिए हैक किया है।

+8

लेखन थ्रेड-सुरक्षित कोड ** यादृच्छिक स्थानों में 'सिंक्रनाइज़' को थप्पड़ मारने से ** अधिक जटिल है। – SLaks

+1

एक साइड नोट के रूप में, लिखित के रूप में आपका 'सुरक्षित स्टेटिक विधि' अभी भी 'स्ट्रिंगबफर' का उपयोग करके सुरक्षित होगा, क्योंकि बफर थ्रेड के बीच साझा नहीं किया जाता है। यह उस विशेष विधि के आमंत्रण के लिए स्थानीय है। – Charlie

+1

उपर्युक्त पर विस्तृत करने के लिए: मूल रूप से कोई नहीं है "यदि एक्स तो वाई" नियम जो थ्रेडसेफ प्रोग्राम लिखने के लिए सार्वभौमिक रूप से मान्य हैं। (जिन लोगों में आप सुनते हैं उनमें से अधिकांश आपके ऐप के लिए अनिवार्य रूप से कम करने के लिए समाप्त हो सकते हैं।) – millimoose

उत्तर

9

नहीं, सभी स्थिर तरीकों को सिंक्रनाइज़ करने की आवश्यकता नहीं है। जहां तक ​​मैं देख सकता हूं आपकी सूची मूल रूप से पूर्ण है। विशेष रूप से सावधान रहें स्थिर विधि या तो

  1. एक स्थिर सदस्य है कि परिवर्तनशील है, या
  2. एक वस्तु है कि संशोधित किया जा सकता के लिए एक संदर्भ पारित हो एक्सेस करता है।

मुझे लगता है कि यह बिना कहने के चला जाता है कि 1 (पहले स्थान पर धागे होने) एक पूर्व शर्त है, क्योंकि बिना धागे synchronize कोई समझ नहीं आता है।

मैंने कभी नहीं सुना है, इसलिए मुझे यकीन नहीं है कि यह एक विचार है या नहीं।

4

नहीं यह सच नहीं है और मुझे यकीन है कि यह हानिकारक होगा। प्रत्येक एप्लिकेशन को समवर्ती होने की आवश्यकता नहीं होती है, और यहां तक ​​कि उन अनुप्रयोगों में भी जो समवर्ती होने की आवश्यकता होती है, कोड के हर टुकड़े को नहीं होना चाहिए।

अधिक सबूत के रूप में, look at the source of String. वहां कई स्थिर तरीके हैं, लेकिन मुझे केवल एक सिंक्रनाइज़ विधि मिल सकती है, और यह भी स्थैतिक नहीं है।

+2

जहां तक ​​स्ट्रिंग सिंक्रनाइज़ नहीं किया जा रहा है, ऐसा इसलिए है क्योंकि स्ट्रिंग अपरिवर्तनीय है - मोटे तौर पर क्योंकि अपरिवर्तनीय वस्तुएं केवल पढ़ने के लिए होती हैं और इसलिए _inrentrent safe_ (मूल प्रश्न में बिंदु 4)। स्ट्रिंग वास्तव में 'सिंक्रनाइज़' के उपयोग से बचने के लिए स्पष्ट रूप से डिज़ाइन की गई कक्षा के सर्वोत्तम उदाहरणों में से एक है और अभी भी सुरक्षित है। – Matt

+2

हां यह सच है, उन्होंने इंगित किया कि बिंदु 4 के साथ, लेकिन सवाल यह है कि ** ** ** सभी ** स्थिर तरीकों को सिंक्रनाइज़ करने की आवश्यकता है और यह एक उदाहरण दिखा रहा है, "नहीं, वे नहीं" –

+1

ओह, वास्तव में। मैं अब समझता हूं - अच्छा उदाहरण :) – Matt

2

स्टेटिक विधियों को लगभग कभी भी वेबपैप में सिंक्रनाइज़ नहीं किया जाना चाहिए। जब तक कि आप 100% सुनिश्चित न हों कि केवल वे लोग जो कभी भी एप्लिकेशन का उपयोग करेंगे, आपकी 3 व्यक्ति लेखांकन टीम हैं, और अगर वे कंपनी से बाहर निकलते हैं और अचानक अचानक गिरने के लिए चेहरे पर लाल होने के इच्छुक हैं।

वैश्विक, अवरुद्ध, साझा संसाधन बनाना स्केलेबिलिटी में कुल विफलता है! यदि आप एप्लिकेशन सर्वर को क्लस्टर करने की आवश्यकता समाप्त करते हैं तो यह आपको बहुत सारे सिरदर्द का कारण बनने जा रहा है और संभवतः आपको टेराकोटा शैली समाधान में लॉक कर देगा।

1

एक वेब एप्लिकेशन (जैसे सर्वलेट/जेएसपी का उपयोग करके एक निर्माण) में, आपको हमेशा एक विधि को सिंक्रनाइज़ करने से बचना चाहिए क्योंकि यह mutli-thread पहुंच के पूरे दर्शन को चुनौती देता है। जगह में, हमेशा एकमात्र आवश्यक कोड डालने का प्रयास करें, जिसे सिंक्रनाइज़ किए गए ब्लॉक के अंदर एक-एक करके एक्सेस किया जाना चाहिए।

1

बिलकुल नहीं। अधिकांशतः, स्थिर विधियां जो मैंने पार की हैं, वे किसी स्थिर चर को संशोधित नहीं करते हैं और इसलिए उन्हें सिंक्रनाइज़ होने की आवश्यकता नहीं है।

सरल समझ के लिए,

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

उपरोक्त विधि धागे की 1000s से कहा जा सकता है और फिर भी यह क्योंकि विधि केवल एक argument- स्ट्रिंग नाम की आवश्यकता है और वह यह है कि धागे की सुरक्षित होने जा रहा है थ्रेड स्टैक से। यह धागे के बीच साझा डेटा नहीं है।

इसके बारे में सोचें, अगर सभी स्थैतिक तरीकों को समेकित किया गया है, तो वेब-अनुप्रयोगों का उपयोग करने के लिए बेहद धीमी और सुस्त होगी। जब भी एक थ्रेड विधि तक पहुंचने का प्रयास करता है तो हमारे पास क्लास-स्तरीय लॉक होगा।

जेडीके द्वारा प्रदान किए गए एपीआई में बहुत से स्थिर तरीके हैं। अगर वे सभी सिंक्रनाइज़ किए गए थे, तो मुझे यकीन है कि हम जावा का उपयोग नहीं करेंगे।

आपके मामले में, वहाँ एक स्थिर चर (कक्षा स्तर चर) है कि स्थिर विधि द्वारा संशोधित किया जा रहा। हां, यदि एकाधिक धागे बनाए जाते हैं और वे स्थिर विधि तक पहुंचने जा रहे हैं, तो थ्रेड हस्तक्षेप की संभावना है। यह थ्रेड-सुरक्षित नहीं है क्योंकि उनके बीच साझा डेटा है।

अधिकतर, स्थिर विधियां उपयोगिता कार्यों को उनके द्वारा पारित तर्कों के आधार पर उपयोगिता कार्य करती हैं।

कृपया ध्यान दें कि गैर-सिंक्रनाइज़ स्थिर स्थैतिक विधियां थ्रेड सुरक्षित हैं यदि वे स्थिर वर्ग चर संशोधित नहीं करते हैं।