कैसे एक धागे से एक अपवाद को पकड़ने के लिए


165

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

यहाँ कोड है:

public class Test extends Thread
{
  public static void main(String[] args) throws InterruptedException
  {
    Test t = new Test();

    try
    {
      t.start();
      t.join();
    }
    catch(RuntimeException e)
    {
      System.out.println("** RuntimeException from main");
    }

    System.out.println("Main stoped");
  }

  @Override
  public void run()
  {
    try
    {
      while(true)
      {
        System.out.println("** Started");

        sleep(2000);

        throw new RuntimeException("exception from thread");
      }
    }
    catch (RuntimeException e)
    {
      System.out.println("** RuntimeException from thread");

      throw e;
    } 
    catch (InterruptedException e)
    {

    }
  }
}

किसी को पता है क्यों?

जवाबों:


220

एक का उपयोग करें Thread.UncaughtExceptionHandler

Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread th, Throwable ex) {
        System.out.println("Uncaught exception: " + ex);
    }
};
Thread t = new Thread() {
    @Override
    public void run() {
        System.out.println("Sleeping ...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("Interrupted.");
        }
        System.out.println("Throwing exception ...");
        throw new RuntimeException();
    }
};
t.setUncaughtExceptionHandler(h);
t.start();

13
यदि मैं अपवाद को ऊपरी स्तर पर फेंकना चाहता हूं, तो मैं क्या कर सकता हूं?
Rodi

6
@rodi एक वाष्पशील चर के लिए पूर्व में सहेजते हैं जो ऊपरी स्तर को हैंडलर (जैसे सदस्य चर) में देख सकते हैं। बाहर, जांचें कि क्या अशक्त है, अन्यथा फेंक दें। या एक नए अस्थिर क्षेत्र के साथ UEH का विस्तार करें और अपवाद को स्टोर करें।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

1
मैं अपने धागे के अंदर से एक अपवाद को पकड़ना चाहता हूं - इसके बिना इसे रोका जा रहा है। क्या यह किसी तरह काम आएगा?
नमस्ते

42

ऐसा इसलिए है क्योंकि अपवाद एक थ्रेड के लिए स्थानीय हैं, और आपका मुख्य थ्रेड वास्तव में runविधि नहीं देखता है । मेरा सुझाव है कि आप थ्रेडिंग कैसे काम करते हैं, इस बारे में अधिक पढ़ें, लेकिन जल्दी से संक्षेप में बताएं: आपका कॉल startएक अलग धागा शुरू करने के लिए है, जो आपके मुख्य धागे से पूरी तरह से असंबंधित है। कॉल joinबस इसके किए जाने की प्रतीक्षा करता है। एक अपवाद जो एक धागे में पिरोया जाता है और कभी नहीं पकड़ा जाता है, उसे समाप्त कर joinदेता है, यही कारण है कि आपके मुख्य धागे पर रिटर्न होता है , लेकिन अपवाद खुद ही खो जाता है।

यदि आप इन अप्रकाशित अपवादों से अवगत होना चाहते हैं, तो आप यह कोशिश कर सकते हैं:

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Caught " + e);
    }
});

बिना अपवाद अपवाद हैंडलिंग के बारे में अधिक जानकारी यहां पाई जा सकती है


मुझे वह पसंद है! हैंडलर को स्टैटिक विधि से सेट करने Thread.setDefaultUncaughtExceptionHandler()से थ्रेड में अपवाद भी पकड़ता है "मुख्य"
Teo J.

38

यह बताता है कि क्या अपवाद हुआ या नहीं, इस पर निर्भर करते हुए धागे का राज्य परिवर्तन:

थ्रेड्स और एक्सेप्शन हैंडलिंग

स्रोत: http://www-public.imtbs-tsp.eu/~gibson/Teaching/CSC7322/L8-ExceptionAndThreads.pdf


12
क्या आपने आरेख बनाया? यदि नहीं, तो स्रोत क्या है?
प्रवाह 2k

23

सबसे अधिक संभावना;

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

हालाँकि, मान लेते हैं कि आपको एक बच्चे के धागे से दूसरे अपवाद को संभालने की आवश्यकता है। मैं इस तरह एक ExecutorService का उपयोग करेगा:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Void> future = executor.submit(new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        System.out.println("** Started");
        Thread.sleep(2000);
        throw new IllegalStateException("exception from thread");
    }
});
try {
    future.get(); // raises ExecutionException for any uncaught exception in child
} catch (ExecutionException e) {
    System.out.println("** RuntimeException from thread ");
    e.getCause().printStackTrace(System.out);
}
executor.shutdown();
System.out.println("** Main stopped");

प्रिंट

** Started
** RuntimeException from thread 
java.lang.IllegalStateException: exception from thread
    at Main$1.call(Main.java:11)
    at Main$1.call(Main.java:6)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
** Main stopped

लेकिन future.get()जब तक धागा निष्पादन को समाप्त नहीं करता है तब तक प्रतीक्षा या ब्लॉक नहीं करता है ?
ग्रेगर वैलेंटाइन

@GregorValentin प्रतीक्षा करें / ब्लॉक करें जब तक कि थ्रेड रननीय / कॉल करने योग्य न हो जाए।
पीटर लॉरी


3

Callableथ्रेड के बजाय का उपयोग करें , फिर आप कॉल कर सकते हैं Future#get()जो किसी भी अपवाद को फेंकता है जिसे कॉल करने योग्य फेंक दिया।


1
ध्यान दें कि अंदर फेंका गया अपवाद Callable.callएक में लपेटा गया है ExcecutionExceptionऔर इसके कारण का मूल्यांकन किया जाना है।
कार्ल रिक्टर

3

वर्तमान में आप केवल RuntimeExceptionएक उप वर्ग को पकड़ रहे हैं Exception। लेकिन आपका आवेदन अपवाद के अन्य उप-वर्गों को फेंक सकता है । Exceptionइसके अलावा सामान्य पकड़ोRuntimeException

चूंकि थ्रेडिंग मोर्चे पर कई चीजों को बदल दिया गया है, उन्नत जावा एपीआई का उपयोग करें।

बहु-थ्रेडिंग जैसे या के लिए अग्रिम java.util.concurrent API को प्राथमिकता दें ।ExecutorServiceThreadPoolExecutor

आप अपवादों को संभालने के लिए अपने ThreadPoolExecutor को कस्टमाइज़ कर सकते हैं।

ओरेकल प्रलेखन पृष्ठ से उदाहरण:

अवहेलना

protected void afterExecute(Runnable r,
                            Throwable t)

विधि दी गई रननीय के निष्पादन के पूरा होने पर आह्वान की गई। यह विधि उस थ्रेड द्वारा लागू की जाती है जिसने कार्य निष्पादित किया है। यदि गैर-अशक्त है, तो थ्रोबेबल अनकंटेड RuntimeException या त्रुटि है जिसके कारण निष्पादन अचानक समाप्त हो गया है।

उदाहरण कोड:

class ExtendedExecutor extends ThreadPoolExecutor {
   // ...
   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         Object result = ((Future<?>) r).get();
       } catch (CancellationException ce) {
           t = ce;
       } catch (ExecutionException ee) {
           t = ee.getCause();
       } catch (InterruptedException ie) {
           Thread.currentThread().interrupt(); // ignore/reset
       }
     }
     if (t != null)
       System.out.println(t);
   }
 }

उपयोग:

ExtendedExecutor service = new ExtendedExecutor();

मैंने उपरोक्त कोड के ऊपर एक कंस्ट्रक्टर जोड़ा है:

 public ExtendedExecutor() { 
       super(1,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
   }

थ्रेड्स की संख्या पर अपनी आवश्यकता के अनुरूप आप इस कंस्ट्रक्टर को बदल सकते हैं।

ExtendedExecutor service = new ExtendedExecutor();
service.submit(<your Callable or Runnable implementation>);

2

मुझे एक ही मुद्दे का सामना करना पड़ा ... थोड़ा काम चारों ओर (केवल कार्यान्वयन के लिए नहीं अनाम वस्तुएं) ... हम कक्षा स्तर की अपवाद वस्तु को शून्य के रूप में घोषित कर सकते हैं ... फिर इसे रन विधि के लिए कैच ब्लॉक के अंदर प्रारंभ करें ... अगर वहाँ रन विधि में त्रुटि थी, यह चर अभेद्य नहीं होगा .. हम इस विशेष चर के लिए अशक्त जाँच कर सकते हैं और यदि यह शून्य नहीं है तो थ्रेड निष्पादन के अंदर अपवाद था।

class TestClass implements Runnable{
    private Exception ex;

        @Override
        public void run() {
            try{
                //business code
               }catch(Exception e){
                   ex=e;
               }
          }

      public void checkForException() throws Exception {
            if (ex!= null) {
                throw ex;
            }
        }
}     

कॉल checkForException () ज्वाइन करने के बाद ()


1

क्या आप setDefaultUncaughtExceptionHandler () और थ्रेड क्लास के एक जैसे तरीकों के साथ खेलते थे? API से: "डिफ़ॉल्ट अनकैप्ड अपवाद हैंडलर को सेट करके, एक एप्लिकेशन उन थ्रेड्स के लिए जिस तरह से अनकवर्ड अपवादों को हैंडल किया जाता है (जैसे किसी विशिष्ट डिवाइस या फ़ाइल में लॉगिंग) को बदल सकता है, जो पहले से ही" डिफ़ॉल्ट "व्यवहार को स्वीकार करेगा।" सिस्टम प्रदान किया गया। "

आपको वहां अपनी समस्या का जवाब मिल सकता है ... शुभकामनाएँ! :-)


1

जावा 8 से भी आप डैन क्रूज़ उत्तर लिख सकते हैं:

Thread t = new Thread(()->{
            System.out.println("Sleeping ...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("Interrupted.");
            }
            System.out.println("Throwing exception ...");
            throw new RuntimeException(); });


t.setUncaughtExceptionHandler((th, ex)-> log(String.format("Exception in thread %d id: %s", th.getId(), ex)));
t.start();

1

AtomicReference भी मुख्य धागे के लिए त्रुटि पारित करने के लिए एक समाधान है। डैन क्रूज़ की तरह एक ही दृष्टिकोण।

AtomicReference<Throwable> errorReference = new AtomicReference<>();

    Thread thread = new Thread() {
        public void run() {
            throw new RuntimeException("TEST EXCEPTION");

        }
    };
    thread.setUncaughtExceptionHandler((th, ex) -> {
        errorReference.set(ex);
    });
    thread.start();
    thread.join();
    Throwable newThreadError= errorReference.get();
    if (newThreadError!= null) {
        throw newThreadError;
    }  

एकमात्र परिवर्तन यह है कि एक अस्थिर चर बनाने के बजाय आप एटॉमिक रीफरेंस का उपयोग कर सकते हैं जो पर्दे के पीछे एक ही काम करता था।


0

इसका विस्तार करना लगभग हमेशा गलत होता है Thread। मैं इसे पर्याप्त रूप से नहीं बता सकता।

बहुविध नियम # 1: विस्तार Threadकरना गलत है। *

यदि आप Runnableइसके बजाय लागू करते हैं तो आप अपने अपेक्षित व्यवहार को देखेंगे।

public class Test implements Runnable {

  public static void main(String[] args) {
    Test t = new Test();
    try {
      new Thread(t).start();
    } catch (RuntimeException e) {
      System.out.println("** RuntimeException from main");
    }

    System.out.println("Main stoped");

  }

  @Override
  public void run() {
    try {
      while (true) {
        System.out.println("** Started");

        Thread.sleep(2000);

        throw new RuntimeException("exception from thread");
      }
    } catch (RuntimeException e) {
      System.out.println("** RuntimeException from thread");
      throw e;
    } catch (InterruptedException e) {

    }
  }
}

पैदा करता है;

Main stoped
** Started
** RuntimeException from threadException in thread "Thread-0" java.lang.RuntimeException: exception from thread
    at Test.run(Test.java:23)
    at java.lang.Thread.run(Thread.java:619)

* जब तक आप अपने एप्लिकेशन थ्रेड का उपयोग करने के तरीके को बदलना चाहते हैं, जो कि 99.9% मामलों में आप नहीं करते हैं। यदि आपको लगता है कि आप 0.1% मामलों में हैं, तो कृपया नियम # 1 देखें।


7
यह मुख्य विधि में अपवाद को नहीं पकड़ता है।
जूल

थ्रेड वर्ग का विस्तार करना बहुत हतोत्साहित करता है। मैंने इसे और स्पष्टीकरण को OJPC प्रस्तुतिकरण में क्यों पढ़ा। पुस्तक ... लगता है, वे जानते हैं कि वे किस बारे में बात कर रहे हैं
luigi7up

2
"मेन से रनटाइम एक्ससेप्शन" यहां कभी नहीं छापा जाता है .. अपवाद मुख्य में नहीं पकड़ा गया है
अमरीश पांडे

0

यदि आप Thread.UncaughtExceptionHandler को क्लास में लागू करते हैं, जो थ्रेड्स को शुरू करता है, तो आप सेट कर सकते हैं और फिर छाँट सकते हैं:

public final class ThreadStarter implements Thread.UncaughtExceptionHandler{

private volatile Throwable initException;

    public void doSomeInit(){
        Thread t = new Thread(){
            @Override
            public void run() {
              throw new RuntimeException("UNCAUGHT");
            }
        };
        t.setUncaughtExceptionHandler(this);

        t.start();
        t.join();

        if (initException != null){
            throw new RuntimeException(initException);
        }

    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        initException =  e;
    }    

}

जो निम्न आउटपुट का कारण बनता है:

Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: UNCAUGHT
    at com.gs.gss.ccsp.enrichments.ThreadStarter.doSomeInit(ThreadStarter.java:24)
    at com.gs.gss.ccsp.enrichments.ThreadStarter.main(ThreadStarter.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.RuntimeException: UNCAUGHT
    at com.gs.gss.ccsp.enrichments.ThreadStarter$1.run(ThreadStarter.java:15)

T.join () सिंक्रनाइज़ होने के बाद से Throwable initException को अस्थिर बनाने की कोई आवश्यकता नहीं है।
निकेल

0

थ्रेड में अपवाद हैंडलिंग: डिफ़ॉल्ट रूप से रन () विधि किसी भी अपवाद को नहीं फेंकती है, इसलिए रन विधि के अंदर सभी चेक किए गए अपवादों को केवल वहां पकड़ा और संभाला जाना चाहिए और रनटाइम अपवादों के लिए हम अनक्रेडिसेप्शनहैंडलर का उपयोग कर सकते हैं। UncaughtExceptionHandler Java द्वारा थ्रेड रन पद्धति में अपवादों को संभालने के लिए प्रदान किया गया एक इंटरफ़ेस है। इसलिए हम इस इंटरफ़ेस को लागू कर सकते हैं और अपने कार्यान्वयन वर्ग को वापस थ्रेड ऑब्जेक्ट पर सेट करके उपयोग कर सकते हैं setUncaughtExceptionHandler () विधि का उपयोग कर। लेकिन इस हैंडलर को ट्रेडर पर स्टार्ट () कॉल करने से पहले सेट करना होगा।

अगर हम uncaughtExceptionHandler को सेट नहीं करते हैं, तो थ्रेड्स थ्रेडग्रुप एक हैंडलर के रूप में कार्य करता है।

 public class FirstThread extends Thread {

int count = 0;

@Override
public void run() {
    while (true) {
        System.out.println("FirstThread doing something urgent, count : "
                + (count++));
        throw new RuntimeException();
    }

}

public static void main(String[] args) {
    FirstThread t1 = new FirstThread();
    t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
        public void uncaughtException(Thread t, Throwable e) {
            System.out.printf("Exception thrown by %s with id : %d",
                    t.getName(), t.getId());
            System.out.println("\n"+e.getClass());
        }
    });
    t1.start();
}
}

Http://coder2design.com/thread-creation/#exception पर दी गई अच्छी व्याख्या


0

RxJava के साथ मेरा समाधान:

@Test(expectedExceptions = TestException.class)
public void testGetNonexistentEntry() throws Exception
{
    // using this to work around the limitation where the errors in onError (in subscribe method)
    // cannot be thrown out to the main thread
    AtomicReference<Exception> ex = new AtomicReference<>();
    URI id = getRandomUri();
    canonicalMedia.setId(id);

    client.get(id.toString())
        .subscribe(
            m ->
                fail("Should not be successful"),
            e ->
                ex.set(new TestException()));

    for(int i = 0; i < 5; ++i)
    {
        if(ex.get() != null)
            throw ex.get();
        else
            Thread.sleep(1000);
    }
    Assert.fail("Cannot find the exception to throw.");
}

0

उन लोगों के लिए जिन्हें सभी थ्रेड्स को चलाने से रोकने और सभी को फिर से चलाने की आवश्यकता होती है, जब उनमें से किसी एक को अपवाद पर रोक दिया जाता है:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {

     // could be any function
     getStockHistory();

}


public void getStockHistory() {

     // fill a list of symbol to be scrapped
     List<String> symbolListNYSE = stockEntityRepository
     .findByExchangeShortNameOnlySymbol(ContextRefreshExecutor.NYSE);


    storeSymbolList(symbolListNYSE, ContextRefreshExecutor.NYSE);

}


private void storeSymbolList(List<String> symbolList, String exchange) {

    int total = symbolList.size();

    // I create a list of Thread 
    List<Thread> listThread = new ArrayList<Thread>();

    // For each 1000 element of my scrapping ticker list I create a new Thread
    for (int i = 0; i <= total; i += 1000) {
        int l = i;

        Thread t1 = new Thread() {

            public void run() {

                // just a service that store in DB my ticker list
                storingService.getAndStoreStockPrice(symbolList, l, 1000, 
                MULTIPLE_STOCK_FILL, exchange);

            }

        };

    Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread thread, Throwable exception) {

                // stop thread if still running
                thread.interrupt();

                // go over every thread running and stop every one of them
                listThread.stream().forEach(tread -> tread.interrupt());

                // relaunch all the Thread via the main function
                getStockHistory();
            }
        };

        t1.start();
        t1.setUncaughtExceptionHandler(h);

        listThread.add(t1);

    }

}

सारांश में :

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


-5

आप ऐसा नहीं कर सकते, क्योंकि यह वास्तव में कोई मतलब नहीं है। यदि आपको नहीं बुलाया गया था t.join()तो आप मुख्य धागा कोड में कहीं भी हो सकते हैं जब tधागा एक अपवाद फेंकता है।

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