क्या रननेबल के रन () को फेंकने का कोई तरीका है?


80

एक विधि जिसे मैं रन () में बुला रहा हूं ( एक कक्षा में जो रननेबल को लागू करता है ) को एक अपवाद फेंकने के लिए डिज़ाइन किया गया है।

लेकिन जावा कंपाइलर मुझे ऐसा नहीं करने देगा और सुझाव देगा कि मैं इसे कोशिश / पकड़ के साथ घेरता हूं।

समस्या यह है कि एक कोशिश / पकड़ के साथ इसे घेरकर मैं उस विशेष रन () को बेकार कर देता हूं । मैं है कि अपवाद फेंक करना चाहते हैं।

यदि मैं रन () केthrows लिए निर्दिष्ट करता हूं , तो कंपाइलर शिकायत करता है ।Exception is not compatible with throws clause in Runnable.run()

आमतौर पर मैं रन नहीं देने के साथ पूरी तरह से ठीक हूं () एक अपवाद को फेंक दो। लेकिन मेरे पास अद्वितीय स्थिति है जिसमें मुझे वह कार्यशीलता होनी चाहिए।

मैं इस सीमा के आसपास कैसे काम कर सकता हूं?


अन्य उत्तरों के अलावा, कार्य की प्रगति को ट्रैक करने के लिए, आप FutureTask वर्ग का उपयोग कर सकते हैं।
जेपीग्रामग्राम

1
गैर-एंड्रॉइड जावा प्रश्न: stackoverflow.com/questions/1369204/…
Ciro Santilli 郝海东:::

जवाबों:


26

यदि आप एक ऐसे वर्ग को उत्तीर्ण करना चाहते हैं Runnableजो Threadरूपरेखा में लागू होता है , तो आपको उस रूपरेखा के नियमों से खेलना होगा, अर्नेस्ट फ्रीडमैन-हिल का उत्तर देखें कि ऐसा क्यों करना एक बुरा विचार है।

हालांकि, मेरे पास एक कूबड़ है, जिसे आप runसीधे अपने कोड में कॉल करना चाहते हैं , इसलिए आपका कॉलिंग कोड अपवाद को संसाधित कर सकता है।

इस समस्या का जवाब आसान है। Runnableथ्रेड लाइब्रेरी से इंटरफ़ेस का उपयोग न करें , बल्कि संशोधित हस्ताक्षर के साथ अपना स्वयं का इंटरफ़ेस बनाएं जो चेक किए गए अपवाद को फेंकने की अनुमति देता है, जैसे

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}

आप एक एडाप्टर भी बना सकते Runnableहैं जो थ्रेड ढांचे में उपयोग के लिए उपयुक्त इस इंटरफ़ेस को वास्तविक (चेक किए गए अपवाद को संभालकर) में परिवर्तित करता है ।


इस तरह के एक सरल समाधान "बॉक्स में" नहीं सोचकर बस पर आ गया। बेशक, Runnableयह एक सरल इंटरफ़ेस है और हम अपना खुद का बना सकते हैं। थ्रेड उपयोग-केस के लिए उपयोगी नहीं है, लेकिन इसके चारों ओर कोड के विभिन्न "रननेबल" विखंडू पास करने के लिए एकदम सही है।
रिचर्ड ले मेसुरियर

82

आप एक का उपयोग कर सकते Callableबजाय, एक को भेजने ExecutorServiceऔर साथ परिणाम के लिए इंतजार FutureTask.isDone()द्वारा लौटाए गए ExecutorService.submit()

जब isDone()सही हो तो आप कॉल करें FutureTask.get()। अब, यदि आपके Callableएक Exceptionतब FutureTask.get()wiill Exceptionभी फेंक दिया है और मूल अपवाद आप का उपयोग करने में सक्षम हो जाएगा Exception.getCause()


23

अगर run()एक जाँच अपवाद को फेंक दिया, तो उसे क्या पकड़ा जाएगा? आपके लिए कोई रास्ता नहीं है कि आप इसे संलग्न करेंrun() कॉल को किसी हैंडलर में , क्योंकि आप उस कोड को नहीं लिखते हैं जो इसे आमंत्रित करता है।

आप run()विधि में अपने चेक किए गए अपवाद को पकड़ सकते हैं , और एक अनछुए अपवाद को फेंक सकते हैं (अर्थात,RuntimeException उसके स्थान पर ) को । यह स्टैक ट्रेस के साथ थ्रेड को समाप्त करेगा; शायद तुम्हारे बाद यही हो।

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


2
पहला भाग अच्छा तर्क नहीं है। "यदि main()एक जाँच अपवाद को फेंक दिया, तो उसे क्या पकड़ा जाएगा?" "अगर run()कोई अनियंत्रित अपवाद फेंक दिया, तो उसे क्या पकड़ा जाएगा?"
ईसाई हुजेर

17

हाँ, वहाँ एक रास्ता एक फेंकने के लिए है जाँच से अपवादrun() विधि तरीका है, लेकिन यह इतना भयानक है कि मैं इसे साझा नहीं करूंगा।

यहां आप इसके बजाय क्या कर सकते हैं; यह उसी तंत्र का उपयोग करता है जो एक रनटाइम अपवाद व्यायाम करेगा:

@Override
public void run() {
  try {
    /* Do your thing. */
    ...
  } catch (Exception ex) {
    Thread t = Thread.currentThread();
    t.getUncaughtExceptionHandler().uncaughtException(t, ex);
  }
}

जैसा कि दूसरों ने नोट किया है, यदि आपकी run()विधि वास्तव में लक्ष्य है Thread, तो अपवाद को फेंकने का कोई मतलब नहीं है क्योंकि यह अप्रमाणिक है; अपवाद को फेंकने का एक ही प्रभाव होता है, अपवाद (कोई नहीं) को फेंकने का नहीं।

यदि यह Threadलक्ष्य नहीं है , तो उपयोग न करें Runnable। उदाहरण के लिए, शायद Callableएक बेहतर फिट है।


लेकिन क्या यह प्रक्रिया को फेंकने पर दुर्घटना का कारण बनता है ??
दिनेश

@DineshVG नहीं, केवल JVM में बग ही सही दुर्घटना का कारण बन सकता है। डिफ़ॉल्ट अपवाद हैंडलर केवल अपवाद प्रिंट करता है। यदि आप इसके बाद की प्रक्रिया से बाहर निकलते हुए देखने के आदी हैं, तो इसका कारण यह है कि केवल धागा ही चल रहा था, और यह समाप्त हो गया।
एरिक्सन

मैंने एक साबुन कॉल के लिए इसका उपयोग करने की कोशिश की (एंड्रॉइड इंस्ट्रूमेंटेशन मामलों में), जहां अगर मुझे साबुन कॉल से 400 मिलता है, तो मैं एक अपवाद फेंक देता हूं। परीक्षण मामले को शुरू करते समय एक थ्रेड से इस साबुन कॉल को बुलाया जाता है। यह थ्रेड t.getUncaughtExceptionHandler().uncaughtException(t, ex);इसे इंस्ट्रूमेंटेशन टेस्ट केस में फेंकने के लिए उपयोग करता है । इस एक पंक्ति को जोड़ने से प्रक्रिया क्रैश हो जाती है! पता नहीं क्यों।
दिनेश

@DineshVG उस माहौल में, क्या Thread.getDefaultUncaughtExceptionHandler()वापसी करता है null? यदि नहीं, तो परिणाम का प्रकार क्या है? यदि कॉल करने के बजाय uncaughtException(), आप चेक किए गए अपवाद को लपेटते हैं RuntimeExceptionऔर फेंक देते हैं तो क्या होता है?
इरिकसन

1
@DineshVG Android ऐसा करने वाले डिफ़ॉल्ट अनकैप्ड अपवाद हैंडलर को सेट कर सकता है। इसलिए मैंने पूछा कि क्या Thread.getDefaultUncaughtExceptionHandler()रिटर्न null; यदि नहीं, तो रिपोर्टिंग प्रदान करने के लिए, एंड्रॉइड एक डिफ़ॉल्ट प्रदान कर रहा है, लेकिन आप इसे सेट कर सकते हैं कि आप क्या चाहते हैं। यहाँ
इरिकसन

6
@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {

    @Override
    default void run() throws RuntimeException {
        try {
            runThrows();
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    void runThrows() throws E;

}

1

कुछ लोग आपको यह समझाने की कोशिश करते हैं कि आपको नियमों से खेलना होगा। सुनो, लेकिन चाहे आप मानें, आपको अपनी स्थिति के आधार पर खुद फैसला करना चाहिए। वास्तविकता यह है कि "आप नियमों द्वारा खेलना चाहिए" ("आप नियमों द्वारा खेलना आवश्यक नहीं")। बस इस बात का ध्यान रखें कि यदि आप नियमों से नहीं खेलते हैं, तो इसके परिणाम हो सकते हैं।

स्थिति न केवल की स्थिति में लागू होती है Runnable, बल्कि जावा 8 के साथ भी अक्सर धाराओं और अन्य स्थानों के संदर्भ में होती है जहां चेक किए गए अपवादों से निपटने की संभावना के बिना कार्यात्मक इंटरफेस पेश किए गए हैं। उदाहरण के लिए, Consumer, Supplier, Function,BiFunction और इतने पर सभी जांचे हुए अपवादों के साथ सौदा करने की सुविधा के बिना घोषित किया गया है।

तो क्या स्थितियां और विकल्प हैं? नीचे दिए गए पाठ में, Runnableकिसी भी कार्यात्मक इंटरफ़ेस का प्रतिनिधि है जो अपवादों की घोषणा नहीं करता है, या अपवादों को हाथ में उपयोग के मामले के लिए भी सीमित घोषित करता है।

  1. आपने Runnableखुद को कहीं घोषित किया है, और Runnableकिसी अन्य चीज़ से बदल सकते हैं।
    1. Runnableसाथ बदलने पर विचार करें Callable<Void>। मूल रूप से एक ही बात है, लेकिन अपवादों को फेंकने की अनुमति है; और return nullअंत में है, जो एक हल्के झुंझलाहट है।
    2. Runnableअपने स्वयं के रिवाज के साथ प्रतिस्थापित करने पर विचार करें जो @FunctionalInterfaceवास्तव में उन अपवादों को फेंक सकते हैं जो आप चाहते हैं।
  2. आपने एक एपीआई का उपयोग किया है, और विकल्प उपलब्ध हैं। उदाहरण के लिए, कुछ जावा एपीआई अतिभारित हैं ताकि आप Callable<Void>इसके बजाय उपयोग कर सकें Runnable
  3. आपने एक एपीआई का उपयोग किया है, और कोई विकल्प नहीं हैं। उस स्थिति में, आप अभी भी विकल्पों से बाहर नहीं हैं।
    1. आप अपवाद को इसमें लपेट सकते हैं RuntimeException
    2. आप अनियंत्रित कलाकारों का उपयोग करके अपवाद को एक RuntimeException में हैक कर सकते हैं।

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

@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
    @Override
    default void run() {
        try {
            tryRun();
        } catch (final Throwable t) {
            throwUnchecked(t);
        }
    }

    private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
        throw (E) t;
    }

    void tryRun() throws Throwable;
}

मैं इसे अधिक पसंद करता हूं new RuntimeException(t)क्योंकि इसमें एक छोटा स्टैक ट्रेस है।

अब आप कर सकते हैं:

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});

अस्वीकरण: इस तरह अनियंत्रित कलाकारों के प्रदर्शन की क्षमता को वास्तव में जावा के भविष्य के संस्करणों में हटाया जा सकता है, जब जेनरिक प्रकार की जानकारी न केवल संकलन समय पर, बल्कि रनटाइम पर भी संसाधित होती है।


0

आपकी आवश्यकता का कोई मतलब नहीं है। यदि आप किसी अपवाद के बारे में थ्रेड को कॉल करना चाहते हैं, तो आप कॉल बैक मैकेनिज्म के माध्यम से ऐसा कर सकते हैं। यह एक हैंडलर या एक प्रसारण या जो कुछ भी आप सोच सकते हैं के माध्यम से हो सकता है।


0

मुझे लगता है कि एक श्रोता पैटर्न इस परिदृश्य के साथ आपकी मदद कर सकता है। आपके run()पद्धति में होने वाले अपवाद के मामले में , एक ट्राइ-कैच ब्लॉक का उपयोग करें और कैच में अपवाद घटना की सूचना भेजें। और फिर अपनी अधिसूचना घटना को संभालें। मुझे लगता है कि यह एक क्लीनर दृष्टिकोण होगा। यह SO लिंक आपको उस दिशा में एक सहायक पॉइंटर देता है।


-1

सबसे आसान तरीका अपनी खुद की अपवाद वस्तु को परिभाषित करना है जो RuntimeExceptionवर्ग के बजाय कक्षा का विस्तार करता है Exception


और जब आप मेरे प्यारे साहब के साथ होंगे तो आप इस RuntimeException को कैसे पकड़ पाएंगे?
डोरमाउस

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