Java ExecutorService görevlerinden özel durumları işleme


213

İş ThreadPoolExecutorparçacıkları sabit sayıda ağır ağırlık görevleri çok sayıda çalıştırmak için Java'nın sınıfını kullanmaya çalışıyorum . Görevlerin her biri, istisnalar nedeniyle başarısız olabileceği birçok yere sahiptir.

Alt sınıfta oldum ThreadPoolExecutorve afterExecutebir görevi çalıştırırken karşılaşılan herhangi bir yakalanmayan istisna sağlamak için gereken yöntemi geçersiz kıldım. Ancak, çalışmasını sağlayamıyorum.

Örneğin:

public class ThreadPoolErrors extends ThreadPoolExecutor {
    public ThreadPoolErrors() {
        super(  1, // core threads
                1, // max threads
                1, // timeout
                TimeUnit.MINUTES, // timeout units
                new LinkedBlockingQueue<Runnable>() // work queue
        );
    }

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if(t != null) {
            System.out.println("Got an error: " + t);
        } else {
            System.out.println("Everything's fine--situation normal!");
        }
    }

    public static void main( String [] args) {
        ThreadPoolErrors threadPool = new ThreadPoolErrors();
        threadPool.submit( 
                new Runnable() {
                    public void run() {
                        throw new RuntimeException("Ouch! Got an error.");
                    }
                }
        );
        threadPool.shutdown();
    }
}

Bu programın çıktısı "Her şey yolunda - durum normal!" iş parçacığı havuzuna gönderilen tek Runnable bir istisna atar. Burada neler olduğuna dair herhangi bir ipucu var mı?

Teşekkürler!


görevin geleceğini, orada ne olduğunu asla sorgulamadınız. Tüm hizmet yürütücü veya program çökmeyecek. Kural dışı durum yakalanır ve ExecutionException altında kaydırılır. Ve eğer future.get () öğesini çağırırsanız yeniden bakacaktır. PS: future.isDone () [Lütfen gerçek api adını okuyun] runnable yanlışlıkla bitse bile true değerini döndürür. Çünkü görev gerçek olarak yapılıyor.
Jai Pandit

Yanıtlar:


156

Gönderen docs :

Not: Eylemler açıkça veya gönderme gibi yöntemlerle (FutureTask gibi) görevlere dahil edildiğinde, bu görev nesneleri hesaplama istisnalarını yakalar ve korur ve böylece ani sonlandırmaya neden olmaz ve dahili istisnalar bu yönteme geçirilmez .

Bir Runnable gönderdiğinizde, bir Geleceğe sarılır.

AfterExecute'iniz şöyle bir şey olmalıdır:

public final class ExtendedExecutor extends ThreadPoolExecutor {

    // ...

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            System.out.println(t);
        }
    }
}

7
Teşekkürler, bu çözümü kullandım. Buna ek olarak, herhangi birinin ilgilenmesi durumunda: diğerleri ExecutorService alt sınıflandırma önerdi, ama yine de yaptım çünkü görevleri bitirmek için beklemek yerine tüm bitmiş ve daha sonra tüm döndürülen Futures get () çağırmak yerine görevleri tamamlamak istedim .
Tom

1
Yürütücüyü alt sınıflamaya başka bir yaklaşım FutureTask'ı alt sınıfa ayırmak ve 'done' yöntemini geçersiz kılmaktır
nos

1
Tom >> Görevleri tamamlandıkça izlemek için örnek snippet kodunuzu ExecutorService alt sınıfına
gönderir misiniz

1
AfterExecute paket özel bir nesne içerdiğinden ve atılabilir nesneye erişmenin hiçbir yolu olmadığından ComplableFuture.runAsync kullanıyorsanız bu yanıt çalışmaz. Çağrıyı sararak etrafta dolaştım. Cevabımı aşağıda görebilirsiniz.
mmm

2
Geleceğin kullanımının tamamlanıp tamamlanmadığını kontrol etmemiz gerekiyor future.isDone()mu? Yana afterExecutesonra çalıştırılır Runnabletamamlandıktan sanırım future.isDone()hep döner true.
Searene

247

UYARI : Bu çözümün çağıran evreyi engelleyeceğine dikkat edilmelidir.


Görev tarafından atılan istisnaları işlemek istiyorsanız, kullanmak Callableyerine genellikle daha iyidir Runnable.

Callable.call() , kontrol edilen istisnaları atmasına izin verilir ve bunlar çağıran evreye geri yayılır:

Callable task = ...
Future future = executor.submit(task);
try {
   future.get();
} catch (ExecutionException ex) {
   ex.getCause().printStackTrace();
}

Bir Callable.call()istisna atarsa, bu bir sarılır ExecutionExceptionve tarafından atılır Future.get().

Bu, alt sınıfa göre daha çok tercih edilir ThreadPoolExecutor. İstisna kurtarılabilir bir durumsa, görevi yeniden gönderme fırsatı da verir.


5
> Callable.call () öğesinin işaretli istisnaları atmasına izin verilir ve bunlar çağıran evreye geri yayılır: Atılan istisnanın yalnızca future.get()aşırı yüklenmiş sürüm çağrıldığında veya çağrıldığında iş parçacığına yayılacağını unutmayın .
2014'te

16
Mükemmel, ama görevleri paralel olarak çalıştırırsam ve yürütmeyi engellemek istemezsem ne yapmalıyım?
Grigory Kislin

44
ExecutorService'in tüm amacını ihlal ettiği için bu çözümü kullanmayın. ExecutorService, arka planda görevleri gerçekleştirebilen asenkron bir yürütme mekanizmasıdır. Çalıştırdıktan hemen sonra future.get () öğesini çağırırsanız, görev tamamlanana kadar çağıran iş parçacığı engellenir.
user1801374

2
Bu çözüm çok yüksek derecelendirilmemelidir. Future.get () eşzamanlı olarak çalışır ve Runnable veya Callable çalıştırılana kadar bir engelleyici görevi görür ve yukarıda belirtildiği gibi, Yürütme Hizmetini kullanma amacını yener
Super Hans

2
#Hile'nin işaret ettiği gibi, bu jdk bir HATA'yı hak ediyor. Future.get () çağrılmazsa Callable'dan yakalanmamış herhangi bir istisna sessizce yok sayılır. Çok kötü tasarım .... sadece kullanılan bir kütüphane ve jdk sessizce istisnalar göz ardı anlamaya 1 + gün geçirdi. Ve bu hala jdk12'de var.
Ben Jiang

18

Bu davranış için açıklama doğru javadoc için geçerlidirExecute :

Not: Eylemler açıkça veya gönderme gibi yöntemlerle (FutureTask gibi) görevlere dahil edildiğinde, bu görev nesneleri hesaplama istisnalarını yakalar ve korur ve böylece ani sonlandırmaya neden olmaz ve dahili istisnalar bu yönteme geçirilmez .


10

İcracıya verilen tedarik edilen runnable'ı sararak etrafta dolaştım.

CompletableFuture.runAsync(() -> {
        try {
              runnable.run();
        } catch (Throwable e) {
              Log.info(Concurrency.class, "runAsync", e);
        }
}, executorService);

3
whenComplete()Yöntemini kullanarak okunabilirliği artırabilirsiniz CompletableFuture.
Eduard Wirch

@EduardWirch Bu işe yarar ancak whenComplete ()
Akshat

7

Tüm istisnaları yutan ve bunları kaydeden jcabi-logVerboseRunnable sınıfını kullanıyorum . Çok uygun, örneğin:

import com.jcabi.log.VerboseRunnable;
scheduler.scheduleWithFixedDelay(
  new VerboseRunnable(
    Runnable() {
      public void run() { 
        // the code, which may throw
      }
    },
    true // it means that all exceptions will be swallowed and logged
  ),
  1, 1, TimeUnit.MILLISECONDS
);

3

Başka bir çözüm ManagedTask ve ManagedTaskListener kullanmak olacaktır .

ManagedTask arabirimini uygulayan bir Callable veya Runnable'a ihtiyacınız var .

Yöntem getManagedTaskListener, istediğiniz örneği döndürür.

public ManagedTaskListener getManagedTaskListener() {

Ve uygulamak ManagedTaskListenertaskDone yöntemi:

@Override
public void taskDone(Future<?> future, ManagedExecutorService executor, Object task, Throwable exception) {
    if (exception != null) {
        LOGGER.log(Level.SEVERE, exception.getMessage());
    }
}

Yönetilen görev yaşam döngüsü ve dinleyici hakkında daha fazla ayrıntı .


2

Bu çalışıyor

  • SingleThreadExecutor'ten türetilmiştir, ancak kolayca adapte edebilirsiniz
  • Java 8 lamdas kodu, ancak düzeltilmesi kolay

Çok sayıda görev alabilen tek bir iş parçacıklı bir Executor yaratacaktır; ve geçerli olanın bir sonraki ile başlamasını bekler

Uncaugth hatası veya istisnası durumunda uncaughtExceptionHandler tarafından yakalanır

public final class SingleThreadExecutorWithExceptions {

    public static ExecutorService newSingleThreadExecutorWithExceptions (son Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {

        ThreadFactory fabrika = (Çalıştırılabilir çalıştırılabilir) -> {
            final Thread newThread = new Thread (çalıştırılabilir, "SingleThreadExecutorWithExceptions");
            newThread.setUncaughtExceptionHandler ((son Konu caugthThread, son Atılabilir atılabilir) -> {
                uncaughtExceptionHandler.uncaughtException (caugthThread, atılabilir);
            });
            yeni dön; iplik;
        };
        yeni dönüş yap FinalizableDelegatedExecutorService
                (yeni ThreadPoolExecutor (1, 1,
                        0L, TimeUnit.MILLISECONDS,
                        yeni LinkedBlockingQueue (),
                        fabrika){


                    Execute (çalıştırılabilir çalıştırılabilir, fırlatılabilir atılabilir)
                        super.afterExecute (çalıştırılabilir, atılabilir);
                        if (throwable == null && runnable Gelecek örneği) {
                            Deneyin {
                                Gelecek gelecek = (Gelecek) koşabilir;
                                if (future.isDone ()) {
                                    future.get ();
                                }
                            } catch (CancellationException ce) {
                                atılabilir = ce;
                            } catch (ExecutionException ee) {
                                throwable = ee.getCause ();
                            } catch (InterruptedException ie) {
                                Thread.currentThread () kesme (.); // yoksay / sıfırla
                            }
                        }
                        if (atılabilir! = null) {
                            uncaughtExceptionHandler.uncaughtException (Thread.currentThread (), throwable);
                        }
                    }
                });
    }



    özel statik sınıf FinalizableDelegatedExecutorService
            DelegatedExecutorService {
        FinalizableDelegatedExecutorService (ExecutorService yöneticisi) {
            Süper (infaz);
        }
        korumalı geçersiz sonlandırma () {
            ) (Super.shutdown;
        }
    }

    / **
     * Yalnızca ExecutorService yöntemlerini gösteren bir sarmalayıcı sınıfı
     * ExecutorService uygulamasının.
     * /
    özel statik sınıf DelegatedExecutorService, AbstractExecutorService {
        özel final İcra Hizmeti e;
        DelegatedExecutorService (ExecutorService yürütücüsü) {e = yürütücü; }
        public void execute (Çalıştırılabilir komut) {e.execute (komut); }
        herkese açık geçersiz kapanma () {e.shutdown (); }
        public List shutdownNow () {return e.shutdownNow (); }
        public boolean isShutdown () {return e.isShutdown (); }
        public boolean isTerminated () {return e.isTerminated (); }
        halka açık boolean bekliyor Bekleme (uzun zaman aşımı, TimeUnit birimi)
                InterruptedException atar {
            return e.awaitTermination (zaman aşımı, birim);
        }
        public Future send (Çalıştırılabilir görev) {
            dönüş e.submit (görev);
        }
        Herkese açık gelecek sunumu (Çağrılabilir görev) {
            dönüş e.submit (görev);
        }
        public Future send (Çalıştırılabilir görev, T sonucu) {
            return e.submit (görev, sonuç);
        }
        herkese açık Liste> invokeAll (Koleksiyon> görevler)
                InterruptedException atar {
            return e.invokeAll (görevler);
        }
        herkese açık Liste> invokeAll (Koleksiyon> görevler,
                                             uzun zaman aşımı, TimeUnit birimi)
                InterruptedException atar {
            return e.invokeAll (görevler, zaman aşımı, birim);
        }
        public T invokeAny (Koleksiyon> görevler)
                InterruptedException, ExecutionException atar {
            return e.invokeAny (görevler);
        }
        public T invokeAny (Koleksiyon> görevler,
                               uzun zaman aşımı, TimeUnit birimi)
                InterruptedException, ExecutionException, TimeoutException {
            return e.invokeAny (görevler, zaman aşımı, birim);
        }
    }



    private SingleThreadExecutorWithExceptions () {}
}

Sadece aranacağınız beri kararsız biraz maalesef sonuçlandırmak kullanma "sonradan zaman çöp toplayıcı toplar o" (veya belki de bir den durumda, bilmiyorum olarak) ...
rogerdpack

1

Görevin yürütülmesini izlemek istiyorsanız, 1 veya 2 iş parçacığını (belki de yüke bağlı olarak daha fazla) döndürebilir ve bir ExecutionCompletionService sarmalayıcısından görevleri almak için kullanabilirsiniz.


0

Eğer senin ExecutorServicebir dış kaynaktan geldiğini (yani o alt sınıf mümkün değildir ThreadPoolExecutorve geçersiz kılma afterExecute(), istediğiniz davranışı elde etmek için dinamik bir proxy kullanabilirsiniz):

public static ExecutorService errorAware(final ExecutorService executor) {
    return (ExecutorService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
            new Class[] {ExecutorService.class},
            (proxy, method, args) -> {
                if (method.getName().equals("submit")) {
                    final Object arg0 = args[0];
                    if (arg0 instanceof Runnable) {
                        args[0] = new Runnable() {
                            @Override
                            public void run() {
                                final Runnable task = (Runnable) arg0;
                                try {
                                    task.run();
                                    if (task instanceof Future<?>) {
                                        final Future<?> future = (Future<?>) task;

                                        if (future.isDone()) {
                                            try {
                                                future.get();
                                            } catch (final CancellationException ce) {
                                                // Your error-handling code here
                                                ce.printStackTrace();
                                            } catch (final ExecutionException ee) {
                                                // Your error-handling code here
                                                ee.getCause().printStackTrace();
                                            } catch (final InterruptedException ie) {
                                                Thread.currentThread().interrupt();
                                            }
                                        }
                                    }
                                } catch (final RuntimeException re) {
                                    // Your error-handling code here
                                    re.printStackTrace();
                                    throw re;
                                } catch (final Error e) {
                                    // Your error-handling code here
                                    e.printStackTrace();
                                    throw e;
                                }
                            }
                        };
                    } else if (arg0 instanceof Callable<?>) {
                        args[0] = new Callable<Object>() {
                            @Override
                            public Object call() throws Exception {
                                final Callable<?> task = (Callable<?>) arg0;
                                try {
                                    return task.call();
                                } catch (final Exception e) {
                                    // Your error-handling code here
                                    e.printStackTrace();
                                    throw e;
                                } catch (final Error e) {
                                    // Your error-handling code here
                                    e.printStackTrace();
                                    throw e;
                                }
                            }
                        };
                    }
                }
                return method.invoke(executor, args);
            });
}

0

Bunun nedeni taşımaktadır AbstractExecutorService :: submitsizin tamamlıyor runnableINTO RunnableFuture(başka bir şey FutureTaskgibi aşağıda)

AbstractExecutorService.java

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null); /////////HERE////////
    execute(ftask);
    return ftask;
}

Sonra executeonu geçecek Workerve Worker.run()aşağıdaki arayacaktır.

ThreadPoolExecutor.java

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();           /////////HERE////////
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

Son olarak task.run();yukarıdaki kod çağrısı çağırır FutureTask.run(). İşte istisna işleyici kodu, bu nedenle beklenen istisna alamıyorsunuz.

class FutureTask<V> implements RunnableFuture<V>

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {   /////////HERE////////
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

0

Bu mmm'nin çözümüne benzer, ancak biraz daha anlaşılabilir. Görevlerinizin run () yöntemini saran soyut bir sınıfı genişletmesini sağlayın.

public abstract Task implements Runnable {

    public abstract void execute();

    public void run() {
      try {
        execute();
      } catch (Throwable t) {
        // handle it  
      }
    }
}


public MySampleTask extends Task {
    public void execute() {
        // heavy, error-prone code here
    }
}

-4

Bunun yerine ThreadPoolExecutor sınıflara, bir ile sağlayacak ThreadFactory yeni konu oluşturur ve bir ile sağlamaktadır Mesela UncaughtExceptionHandler


3
Ben de denedim, ama uncaughtException yöntemi asla çağrı almak gibi görünüyor. Bunun ThreadPoolExecutor sınıfındaki bir işçi iş parçacığının özel durumları yakaladığına inanıyorum.
Tom

5
UncaughtException yöntemi çağrılmaz, çünkü ExecutorService'in gönderme yöntemi Gelecekteki Callable / Runnable öğesini kaydırır; istisna orada yakalanıyor.
Emil Sit
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.