यदि कोई ऑब्जेक्ट लॉक (सिंक्रनाइज़) है तो जावा में ब्लॉक न करें, यह कैसे निर्धारित किया जाए?


81

मेरे पास एक प्रक्रिया है जिसमें रिकॉर्ड के एक सेट (रिकॉर्डा, रिकॉर्डबी, आदि ...) के साथ स्मृति में एक तालिका होती है।

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

वर्तमान में मैं ऐसा कुछ करता हूं:

synchronized(record)
{
performOperation(record);
}

लेकिन इससे मुझे समस्या हो रही है ... क्योंकि Process1 ऑपरेशन कर रहा है, अगर Process2 इसमें आता है / सिंक्रनाइज़ स्टेटमेंट पर ब्लॉक करता है और जब Process1 समाप्त हो जाता है तो यह ऑपरेशन करता है। इसके बजाय मुझे कुछ ऐसा चाहिए:

if (record is locked)
   return;

synchronized(record)
{
performOperation(record);
}

यह कैसे पूरा किया जा सकता है पर कोई सुराग? कोई भी सहायताकाफी प्रशंसनीय होगी। धन्यवाद,

जवाबों:


87

एक बात ध्यान देने वाली है कि आपको इस तरह की सूचना तुरंत मिलती है, यह बासी है। दूसरे शब्दों में, आपको बताया जा सकता है कि किसी के पास ताला नहीं है, लेकिन तब जब आप इसे हासिल करने की कोशिश करते हैं, तो आप ब्लॉक कर देते हैं क्योंकि दूसरे धागे ने चेक के बीच ताला निकाल लिया और आप इसे हासिल करने की कोशिश कर रहे हैं।

ब्रायन को इंगित करना सही है Lock, लेकिन मुझे लगता है कि आप वास्तव में चाहते हैं कि इसकी tryLockविधि क्या है:

Lock lock = new ReentrantLock();
......
if (lock.tryLock())
{
    // Got the lock
    try
    {
        // Process record
    }
    finally
    {
        // Make sure to unlock so that we don't cause a deadlock
        lock.unlock();
    }
}
else
{
    // Someone else had the lock, abort
}

आप tryLockप्रतीक्षा करने के लिए समय की एक राशि के साथ भी कॉल कर सकते हैं - इसलिए आप इसे एक सेकंड के दसवें के लिए प्राप्त करने का प्रयास कर सकते हैं, फिर यदि आप इसे प्राप्त नहीं कर सकते हैं (उदाहरण के लिए)।

(मुझे लगता है कि यह एक अफ़सोस की बात है कि जावा एपीआई ऐसा नहीं करता है - जहां तक ​​मुझे जानकारी है - "अंतर्निहित" लॉकिंग के लिए समान कार्यक्षमता प्रदान करते हैं, जैसा कि Monitorवर्ग .NET में करता है। फिर से, बहुत सारे हैं। अन्य चीजों को मैं दोनों प्लेटफार्मों में नापसंद करता हूं जब यह थ्रेडिंग की बात आती है - उदाहरण के लिए हर वस्तु संभावित रूप से एक मॉनिटर है!)


हाँ। ये एक अच्छा बिंदु है। मैंने उदाहरण कोड का शाब्दिक अर्थ लिया, जबकि ऊपर निश्चित रूप से एक अधिक मजबूत कार्यान्वयन है
ब्रायन एग्न्यू

5
लेकिन मैं प्रति रिकॉर्ड लॉक का उपयोग कैसे करूं? वर्तमान में रिकॉर्ड को रिकॉर्ड के एक हैशटेबल में संग्रहीत किया जाता है ... तो मुझे ताले के मिलान वाले हैशटेबल की आवश्यकता है? मैं यह सुनिश्चित करने की कोशिश कर रहा हूं कि मेरे पास सबसे अधिक संभव संगोष्ठी है, इसलिए यदि कोई प्रक्रिया रिकॉर्डसी का उपयोग करना चाहती है जो ठीक होना चाहिए (यदि केवल रिकॉर्डबी लॉक है) - मैं एक वैश्विक लॉक का उपयोग करता हूं तो यह अनिवार्य रूप से पूरे हैशटेबल को लॉक करने के समान है। ... कि कोई मतलब है?
Shaitan00

@ Shaitan00: सबसे आसान तरीका यह होगा कि रिकॉर्ड के भीतर एक लॉक हो। मूल रूप से आप प्रत्येक रिकॉर्ड से जुड़ा एक लॉक चाहते हैं - इसलिए इसे ऑब्जेक्ट में डालें।
जॉन स्कीट

जाहिर है :) मुझे लगता है कि मुझे अनलॉक करने का प्रबंधन करने की आवश्यकता नहीं है? मतलब जब यह .tryLock () के {} से बाहर निकलता है तो यह स्वचालित रूप से और तुरंत ही सही अनलॉक हो जाएगा?
शैतान

1
आपको अनलॉकिंग प्रबंधित करने की आवश्यकता है। अपने उत्तर में संदर्भित ट्यूटोरियल और अनलॉक () विधि को अंत में देखें {} ब्लॉक
ब्रायन एग्न्यू

24

जावा 5 कंसर्टिफ़िकेशन पैकेज में शुरू की गई लॉक ऑब्जेक्ट्स पर एक नज़र डालें ।

जैसे

Lock lock = new ReentrantLock()
if (lock.tryLock()) {
   try {
      // do stuff using the lock...
   }
   finally {
      lock.unlock();
   }
}
   ...

ReentrantLock वस्तु अनिवार्य रूप से पारंपरिक रूप में एक ही बात कर रही है synchronizedतंत्र है, लेकिन अधिक कार्यक्षमता के साथ।

EDIT: जैसा कि जॉन ने उल्लेख किया है, isLocked()विधि आपको उस पल में बताती है , और उसके बाद वह जानकारी पुरानी हो जाती है। TryLock () विधि और अधिक विश्वसनीय आपरेशन (ध्यान दें कि आप के साथ ही एक समय समाप्ति के साथ इस का उपयोग कर सकते हैं) दे देंगे

EDIT # 2: उदाहरण में अब tryLock()/unlock()स्पष्टता शामिल है।


11

मुझे यह मिला, हम Thread.holdsLock(Object obj)जांच कर सकते हैं कि कोई वस्तु बंद है या नहीं:

रिटर्न trueऔर अगर केवल वर्तमान थ्रेड निर्दिष्ट ऑब्जेक्ट पर मॉनिटर लॉक रखता है।

ध्यान दें कि Thread.holdsLock()रिटर्न falseअगर ताला द्वारा आयोजित किया जाता है कुछ और बुला धागा धागा कि ताला धारण नहीं है।


8

जबकि लॉक ऑब्जेक्ट का उपयोग करने वाले उपरोक्त दृष्टिकोण को करने का सबसे अच्छा तरीका है, अगर आपको मॉनिटर का उपयोग करके लॉकिंग की जांच करने में सक्षम होना है, तो यह किया जा सकता है। हालाँकि, यह स्वास्थ्य चेतावनी के साथ आता है क्योंकि तकनीक गैर-ओरेकल जावा वीएमएस के लिए पोर्टेबल नहीं है और यह भविष्य के वीएम संस्करणों में टूट सकती है क्योंकि यह समर्थित सार्वजनिक एपीआई नहीं है।

यहां है कि इसे कैसे करना है:

private static sun.misc.Unsafe getUnsafe() {
    try {
        Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public void doSomething() {
  Object record = new Object();
  sun.misc.Unsafe unsafe = getUnsafe(); 
  if (unsafe.tryMonitorEnter(record)) {
    try {
      // record is locked - perform operations on it
    } finally {
      unsafe.monitorExit(record);
    }
  } else {
      // could not lock record
  }
}

मेरी सलाह है कि आप इस दृष्टिकोण का उपयोग तभी करेंगे जब आप इसके लिए java.util.concurrent लॉक ऑब्जेक्ट का उपयोग करने के लिए अपने कोड को रिफलेक्टर नहीं कर सकते और यदि आप Oracle VM पर चल रहे हैं।


12
@alestanis, मैं सहमत नहीं हूँ। यहाँ मैं आज इन उत्तरों को पढ़ रहा हूं और सभी उत्तर / टिप्पणियों के लिए खुश हूं, जब भी उन्हें दिया जाता है तो कोई बात नहीं।
टॉम

@ ये उत्तर SO सिस्टम द्वारा चिह्नित किए गए हैं, इसीलिए मैंने एक संदेश छोड़ा है। जब आप समीक्षा शुरू करेंगे तो आप देखेंगे :)
alestanis

2
@alestanis, मुझे लगता है कि यह दुर्भाग्यपूर्ण है - मैं अक्सर पुराने प्रश्नों को देखता हूं जिनके पास स्वीकृत उत्तर होते हैं, लेकिन उनके पास नए, बेहतर उत्तर भी होते हैं क्योंकि तकनीकी परिवर्तन या क्योंकि स्वीकृत उत्तर वास्तव में पूरी तरह से सही नहीं था।
टॉम

1
यह धागा पुराना होने और अच्छा जवाब होने के अलावा, यह उत्तर यहाँ अच्छा नहीं है। यदि आप वास्तव में एक मॉनिटर का उपयोग कर लॉक करना चाहते हैं, तो आप थ्रेड। होमलॉक (ऑब्जेक्ट) का उपयोग कर सकते हैं।
टोबियास

3

जबकि लॉक उत्तर बहुत अच्छे हैं, मैंने सोचा कि मैं एक अलग डेटा संरचना का उपयोग करके एक विकल्प पोस्ट करूंगा। अनिवार्य रूप से, आपके विभिन्न सूत्र यह जानना चाहते हैं कि कौन से रिकॉर्ड लॉक हैं और कौन से नहीं हैं। ऐसा करने का एक तरीका लॉक रिकॉर्ड्स का ट्रैक रखना है और यह सुनिश्चित करना है कि लॉक किए गए सेट में रिकॉर्ड जोड़ने के लिए डेटा संरचना का सही परमाणु संचालन है।

मैं एक उदाहरण के रूप में CopyOnWriteArrayList का उपयोग करूंगा क्योंकि यह चित्रण के लिए कम "जादू" है। CopyOnWriteArraySet एक अधिक उपयुक्त संरचना है। यदि आपके पास औसतन एक ही समय में बहुत सारे और बहुत सारे रिकॉर्ड लॉक हैं तो इन कार्यान्वयनों के साथ प्रदर्शन निहितार्थ हो सकते हैं। एक ठीक से सिंक्रनाइज़ हैशसेट भी काम करेगा और ताले संक्षिप्त हैं।

मूल रूप से, उपयोग कोड इस तरह दिखेगा:

CopyOnWriteArrayList<Record> lockedRecords = ....
...
if (!lockedRecords.addIfAbsent(record))
    return; // didn't get the lock, record is already locked

try {
    // Do the record stuff
}        
finally {
    lockedRecords.remove(record);
}

यह आपको प्रति रिकॉर्ड लॉक का प्रबंधन करने से रोकता है और एक ही स्थान प्रदान करता है कि सभी लॉक को किसी कारण से आवश्यक होना चाहिए। दूसरी ओर, यदि आपके पास कभी भी एक से अधिक रिकॉर्ड हैं, तो सिंक्रनाइज़ेशन के साथ एक वास्तविक हैशट बेहतर हो सकता है क्योंकि लीनियर के बजाय ऐड / रिमूवल लुक-अप O (1) होगा।

बस चीजों को देखने का एक अलग तरीका है। बस इस बात पर निर्भर करता है कि आपकी वास्तविक थ्रेडिंग आवश्यकताएं क्या हैं। व्यक्तिगत रूप से, मैं एक Collections.synchronizedSet (नया HashSet ()) का उपयोग करूँगा क्योंकि यह वास्तव में तेज़ होगा ... इसका एकमात्र अर्थ यह है कि धागे तब उपज सकते हैं जब वे अन्यथा नहीं होंगे।


यदि आप पूर्ण नियंत्रण चाहते हैं, तो आप इसे अपनी ही Lockकक्षा में रख सकते हैं जिसका मुझे अनुमान है।
bvdb

1

एक अन्य समाधान यह है कि (अगर आपके पास यहां दिए गए उत्तरों के साथ मौका नहीं है) टाइमआउट का उपयोग कर रहा है। 1 से नीचे लटकने पर एक के बाद एक शून्य वापस आ जाएगा:

ExecutorService executor = Executors.newSingleThreadExecutor();
        //create a callable for the thread
        Future<String> futureTask = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return myObject.getSomething();
            }
        });

        try {
            return futureTask.get(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            //object is already locked check exception type
            return null;
        }

-2

इसके लिए धन्यवाद, इसने मुझे दौड़ की स्थिति को हल करने में मदद की। मैंने बेल्ट और सस्पेंडर्स दोनों पहनने के लिए इसे थोड़ा बदल दिया।

तो यहाँ मेरा सुझाव स्वीकार किए गए उत्तर के एक सुधार के लिए मेरा सुझाव है:

आप यह सुनिश्चित कर सकते हैं कि आपको tryLock()इस तरह से कुछ करने की विधि तक सुरक्षित पहुँच मिले :

  Lock localLock = new ReentrantLock();

  private void threadSafeCall() {
    boolean isUnlocked = false;

    synchronized(localLock) {
      isUnlocked = localLock.tryLock();
    }

    if (isUnlocked) {
      try {
        rawCall();
      }
      finally {
        localLock.unlock();
      }
    } else {
      LOGGER.log(Level.INFO, "THANKS! - SAVED FROM DOUBLE CALL!");
    }
  }

यह उस स्थिति से बचता है जहां आपको tryLock()लगभग एक ही समय में दो कॉलिंग मिल सकती हैं , जिससे वापसी संभावित रूप से संदिग्ध हो सकती है। मुझे लगता है कि अगर मैं गलत हूं, तो मैं यहां पर सतर्क हो सकता हूं। लेकिन नमसते! मेरी टमटम अब स्थिर है :-) ।।

मेरे ब्लॉग पर मेरे विकास के मुद्दों पर अधिक पढ़ें ।


1
लॉक ऑब्जेक्ट पर सिंक्रनाइज़ का उपयोग करना बहुत, बहुत बुरा है। एक लॉक का पूरा बिंदु सिंक्रनाइज़ से बचने के लिए है, इसलिए जब आप लॉक प्राप्त नहीं कर सकते हैं तो आप टाइमआउट या जल्दी से लौट सकते हैं।
अजाक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.