Bir iş parçacığından bir özel durum nasıl yakalanır


165

Java ana sınıf var, sınıfta, ben yeni bir iş parçacığı başlatmak, ana, iş parçacığı ölene kadar bekler. Bazı anda, iş parçacığı bir çalışma zamanı özel durum atmak, ancak ana sınıftaki iş parçacığından atılan özel durumu yakalayamıyorum.

İşte kod:

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)
    {

    }
  }
}

Nedenini bilen var mı?

Yanıtlar:


220

A kullanın 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
İstisnayı bir üst düzeye çıkarmak istersem ne yapabilirim?
rodi

6
@ rodi ex'i işleyicide üst seviyenin görebileceği değişken bir değişkene kaydeder (örneğin üye değişkeni). Dışarıda, boş olup olmadığını kontrol edin, aksi takdirde atın. Veya UEH'yi yeni bir değişken alanla genişletin ve istisnayı orada saklayın.
Ciro Santilli 法轮功 冠状 :0

1
Ben iş parçacığı içinde bir istisna yakalamak istiyorum - durmadan. Bu bir şekilde işe yarar mı?
Lealo

42

Bunun nedeni, istisnaların bir iş parçacığı için yerel olmasıdır ve ana iş parçacığınız aslında runyöntemi görmez . İplik geçirmenin nasıl çalıştığı hakkında daha fazla bilgi vermenizi öneririm, ancak hızlı bir şekilde özetlemek gerekirse: startana iş parçanızla tamamen alakasız farklı bir iş parçacığı başlatma çağrınız . Çağrı çağrısı joinsadece bunun yapılmasını bekler. Bir iş parçacığına atılan ve hiç yakalanmayan bir istisna onu sonlandırır, bu yüzden joinana iş parçacığınıza geri döner, ancak istisnanın kendisi kaybolur.

Bu yakalanmamış istisnaların farkında olmak istiyorsanız şunu deneyebilirsiniz:

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

Yakalanmayan özel durum işleme hakkında daha fazla bilgiyi burada bulabilirsiniz .


Bunu sevdim! İşleyiciyi statik yöntemle ayarlamak, Thread.setDefaultUncaughtExceptionHandler()"ana" iş parçacığındaki istisnaları da yakalar
Teo J.


23

Büyük ihtimalle;

  • istisnayı bir iş parçacığından diğerine geçirmeniz gerekmez.
  • bir istisnayı ele almak istiyorsanız, onu atmış olan iş parçacığında yapın.
  • ana iş parçacığınızın bu örnekte arka plan iş parçacığından beklemesi gerekmez, bu aslında bir arka plan iş parçacığına ihtiyacınız olmadığı anlamına gelir.

Ancak, başka bir iş parçacığından bir istisnayı ele almanız gerektiğini varsayalım. Böyle bir ExecutorService kullanmak istiyorum:

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

baskılar

** 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

Ancak future.get()iş parçacığı yürütme bitene kadar beklemez veya engellemez mi?
Gregor Valentin

@GregorValentin iş parçacığı çalıştırılabilir / çağrılabilir bitene kadar bekler / engeller.
Peter Lawrey


3

İş Callableparçacığı yerine kullanın , sonra Future#get()Callable attı herhangi bir istisna atar hangi arayabilirsiniz .


1
İçine atılan istisnanın bir içine Callable.callsarıldığını ExcecutionExceptionve nedeninin değerlendirilmesi gerektiğini unutmayın.
Karl Richter

3

Şu anda sadece RuntimeExceptionbir alt sınıf yakalamak Exception. Ancak uygulamanız diğer İstisna alt sınıflarını da atabilir . ExceptionEk olarak genel yakalaRuntimeException

İş Parçacığı cephesinde birçok şey değiştiğinden, gelişmiş java API'sini kullanın.

Veya gibi çoklu iş parçacıkları için avans java.util.concurrent API'yi tercih edin .ExecutorServiceThreadPoolExecutor

ThreadPoolExecutor'unuzu istisnaları ele alacak şekilde özelleştirebilirsiniz .

Oracle Belge Sayfası Örneği:

Geçersiz Kıl

protected void afterExecute(Runnable r,
                            Throwable t)

Verilen Runnable'ın yürütülmesi tamamlandıktan sonra çağrılan yöntem. Bu yöntem, görevi yürüten iş parçacığı tarafından çağrılır. Null değilse, Throwable, yürütmenin aniden sonlanmasına neden olan yakalanmamış RuntimeException veya Error öğesidir.

Örnek kod:

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

Kullanımı:

ExtendedExecutor service = new ExtendedExecutor();

Yukarıdaki kod üstünde bir kurucu ekledim:

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

Bu kurucuyu iş parçacığı sayısı gereksiniminize uyacak şekilde değiştirebilirsiniz.

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

2

Aynı sorunla karşı karşıya kaldım ... çok az çalışma (sadece anonim nesneler değil uygulama için) ... sınıf düzeyinde istisna nesnesini null olarak ilan edebiliriz ... sonra çalıştırma yöntemi için catch bloğu içinde başlatabiliriz ... varsa çalışma yönteminde hata oldu, bu değişken null olmayacak .. sonra bu belirli değişken için null kontrol olabilir ve onun null değilse o zaman iş parçacığı yürütme içinde istisna vardı.

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

join () öğesinden sonra checkForException () öğesini çağırın


1

SetDefaultUncaughtExceptionHandler () ve Thread sınıfının benzer yöntemleri ile oynadınız mı? API'dan: "Bir uygulama, varsayılan yakalanmayan istisna işleyicisini ayarlayarak," varsayılan "davranışı zaten kabul eden iş parçacıkları için yakalanmayan istisnaların işlenme şeklini (belirli bir aygıta veya dosyaya oturum açma gibi) değiştirebilir. sistemi sağlandı. "

Sorunun cevabını orada bulabilirsin ... iyi şanslar! :-)


1

Ayrıca Java 8'den Dan Cruz yanıtını şöyle yazabilirsiniz:

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 ayrıca hatayı ana iş parçacığına aktarmak için bir çözümdür. Dan Cruz'da olduğu gibi aynı yaklaşımdır.

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

Tek değişiklik, değişken bir değişken oluşturmak yerine sahnelerin arkasında aynı şeyi yapan AtomicReference'ı kullanabilmenizdir.


0

Uzatmak neredeyse her zaman yanlıştır Thread. Bunu yeterince güçlü bir şekilde ifade edemem.

Çoklu iş parçacığı Kural # 1: Genişletme Threadyanlış. *

RunnableBunun yerine uygularsanız , beklenen davranışınızı görürsünüz.

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) {

    }
  }
}

üretir;

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)

* uygulamanızın iş parçacıklarını kullanma şeklini değiştirmek istemiyorsanız,% 99.9'unda kullanmazsınız. Vakaların% 0.1'inde olduğunuzu düşünüyorsanız, lütfen kural 1'e bakın.


7
Bu, ana yöntemdeki istisnayı yakalamaz.
philwb

Thread sınıfını genişletmek kesinlikle önerilmez. Bu ve OJPC hazırlık neden açıklamasını okudum. kitap ... Tahmin et, ne hakkında konuştuklarını biliyorlar
luigi7up

2
"RuntimeException from main" hiçbir zaman buraya basılmaz .. istisna ana yakalanmaz
Amrish Pandey

0

Thread'ı başlatan Thread.UncaughtExceptionHandler öğesini sınıfta uygularsanız, istisnayı ayarlayabilir ve daha sonra yeniden kullanabilirsiniz:

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

}

Bu, aşağıdaki çıktıya neden olur:

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)

Throwable initException özelliğini geçici hale getirmeye gerek yoktur, çünkü t.join () eşitlenir.
21'de NickL

0

İş parçacığında kural dışı durum işleme: Varsayılan olarak run () yöntemi herhangi bir istisna oluşturmaz, bu nedenle çalıştırma yöntemi içindeki tüm denetlenen istisnalar yalnızca yakalanmalı ve işlenmelidir ve çalışma zamanı istisnaları için UncaughtExceptionHandler kullanabiliriz. UncaughtExceptionHandler, bir Thread çalıştırma yöntemindeki istisnaları işlemek için Java tarafından sağlanan bir arabirimdir. Böylece bu arabirimi uygulayabilir ve setUncaughtExceptionHandler () yöntemini kullanarak uygulayıcı sınıfımızı Thread nesnesine geri ayarlayabiliriz. Ancak bu işleyicinin lastik sırtında start () çağrılmadan önce ayarlanması gerekir.

uncaughtExceptionHandler öğesini ayarlamazsak, Thread ThreadGroup işleyici olarak işlev görür.

 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/#exceptions adresinde verilen güzel açıklama


0

RxJava ile çözümüm:

@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

İstisnada herhangi biri durdurulduğunda tüm Konuların çalışmasını durdurması ve yeniden çalıştırması gerekenler için :

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

    }

}

Sonuç olarak :

Birden çok iş parçacığı oluşturan bir ana işleve sahipsiniz, her birinde bir iş parçacığının içindeki herhangi bir Özel Durum tarafından tetiklenen UncaughtExceptionHandler vardır. Her iş parçacığını listeye eklersiniz. Bir UncaughtExceptionHandler tetiklenirse, Liste boyunca döngü yapacaktır, her İş Parçacığını durdurur ve ana işlev rekreasyonunu tüm İş Parçasını yeniden başlatır.


-5

Gerçekten mantıklı olmadığı için bunu yapamazsınız. Eğer aramadıysanız t.join(), tiş parçacığı bir istisna attığında ana iş parçacığı kodun herhangi bir yerinde olabilir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.