ExecutorService gönderimi ve ExecutorService yürütmesi arasında seçim yapın


194

Nasıl arasında bir seçim yapmalı ExecutorService en göndermek veya yürütme döndürülen değer benim endişe değilse?

Her ikisini de test edersem, döndürülen değer dışında ikisi arasında herhangi bir fark görmedim.

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());

Yanıtlar:


204

İstisna / hata işleme ile ilgili bir fark vardır.

execute()Bazıları Throwabletarafından kuyruğa alınan bir görev UncaughtExceptionHandler, Threadçalıştırılan görevin çağrılmasına neden olur . Herhangi bir özel işleyici kurulmamışsa UncaughtExceptionHandler, Throwableyığın izlemesini tipik olarak yazdıran varsayılan System.errçağrı yapılır.

Öte yandan, bir ThrowableSüreç tarafından üretilen ile sıraya submit()irade bağlamak Throwableiçin Futureyapılan çağrı üretilen olduğunu submit(). Arayan get()bu konuda Futurebir atacağım ExecutionExceptionorjinali ile Throwable(çağrılarak erişilebilen kendi nedeni olarak getCause()üzerinde ExecutionException).


19
Bu davranışın herhangi bir kontrolünüz olmayabilir, Runnablesarılıp sarılmayacağına bağlı olup olmadığının garanti edilmediğini unutmayın Task. Örneğin, eğer Executorgerçekten bir a ScheduledExecutorServiceise, göreviniz dahili olarak bir sarılır Futureve yakalanmayanlar Throwablebu nesneye bağlanır.
rxg

4
FutureTabii ki 'sarılı olsun ya da olmasın' demek istiyorum . Örneğin, ScheduledThreadPoolExecutor # execute için Javadoc'a bakın .
rxg

61

execute : Yangın için kullanın ve çağrıları unutun

gönder : Yöntem çağrısının sonucunu incelemek ve çağrıFuturetarafından döndürülen itirazüzerine uygun işlemi yapmak için kullanın

Gönderen javadocs

submit(Callable<T> task)

Yürütme için değer döndüren bir görev gönderir ve görevin bekleyen sonuçlarını temsil eden bir Gelecek döndürür.

Future<?> submit(Runnable task)

Yürütülebilir bir yürütme görevi gönderir ve bu görevi temsil eden bir Gelecek döndürür.

void execute(Runnable command)

Verilen komutu gelecekte bir zamanda yürütür. Komut, Yürütücü uygulamasının takdirine bağlı olarak yeni bir iş parçacığında, birleştirilmiş bir iş parçacığında veya çağıran iş parçacığında yürütülebilir.

Kullanırken önlem almanız gerekir submit(). Görev kodunuzu try{} catch{}bloğa gömmediğiniz sürece istisna çerçevenin kendisinde gizler .

Örnek kod: Bu kod yutulur Arithmetic exception : / by zero.

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(10);
        //ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

çıktı:

java ExecuteSubmitDemo
creating service
a and b=4:0

Aynı kod () submit()ile değiştirilerek atılır execute:

değiştirmek

service.submit(new Runnable(){

ile

service.execute(new Runnable(){

çıktı:

java ExecuteSubmitDemo
creating service
a and b=4:0
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:14)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:744)

Submit () kullanılırken bu tür senaryolar nasıl ele alınır?

  1. {} Catch {} blok kodunu deneyin ile Görev kodunuzu ( Çalıştırılabilir veya Çağrılabilir uygulama) gömün
  2. uygulamak CustomThreadPoolExecutor

Yeni çözüm:

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        //ExecutorService service = Executors.newFixedThreadPool(10);
        ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

class ExtendedExecutor extends ThreadPoolExecutor {

   public ExtendedExecutor() { 
       super(1,1,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
   }
   // ...
   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);
   }
 }

çıktı:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero

İyi net açıklama. Her ne kadar uzanan gerçekten gerekli DEĞİL. Görevin başarılı olup olmadığını bilmek için gelecekteki nesne tüketilmelidir. Bu nedenle, Gelecek <t> tüketmeyi planlıyorsanız, gönder () kullanın, aksi takdirde execute () kullanın
13'te prash

11

dönüş türüyle ilgilenmiyorsanız, execute komutunu kullanın. sadece Gelecek'in geri dönüşü olmadan, gönderme ile aynıdır.


15
Kabul edilen cevaba göre bu doğru değil. İstisna yönetimi oldukça önemli bir farktır.
Sıfır3

7

Javadoc'tan Alınan:

Yöntem submit, executeyürütmeyi iptal etmek ve / veya tamamlanmasını beklemek için kullanılabilecek bir {@link Gelecek} oluşturarak ve döndürerek temel yöntemi {@link Executor # } genişletir .

Şahsen ben yürütmeyi tercih ederim çünkü bu gerçekten kişisel bir tercih meselesi olmasına rağmen, daha açıklayıcı hissediyor.

Daha fazla bilgi vermek için: ExecutorServiceuygulama durumunda , çağrıyla geri döndürülen temel uygulama Executors.newSingleThreadedExecutor()a ThreadPoolExecutor.

submitÇağrılar onun ebeveyn tarafından sağlanan AbstractExecutorServiceve tüm çağrı dahili olarak yürütün. ThreadPoolExecutordoğrudan yürütme geçersiz kılınır / sağlanır .


2

Gönderen Javadoc :

Komut, Yürütücü uygulamasının takdirine bağlı olarak yeni bir iş parçacığında, birleştirilmiş bir iş parçacığında veya çağıran iş parçacığında yürütülebilir.

Böylece, uygulamaya bağlı olarak Executorgörev yürütülürken gönderme iş parçacığı blokları bulabilirsiniz.


1

Tam cevap, burada yayınlanan iki cevabın bir bileşimidir (artı biraz "ekstra"):

  • Bir görevi göndererek (yerine getirerek) sonucu elde etmek veya eylemi iptal etmek için kullanılabilecek bir geleceği geri alırsınız. Ne zaman bu tür bir kontrole sahip değilsiniz execute(çünkü dönüş türü kimliği void)
  • executebir Runnablesüre beklemek için submita Runnableveya a Callabledeğişkenini bağımsız değişken olarak alabilir (ikisi arasındaki fark hakkında daha fazla bilgi için aşağıya bakın).
  • executekontrol edilmeyen istisnaları hemen patlatır (kontrol edilen istisnaları atamaz !!!), sonuç olarak geri gelen geleceğe her türlü istisnayı submitbağlar ve sadece (sarılmış) istisna çağrıldığında atılır. Alacağınız Throwable bir örneğidir ve bu nesnenin adını çağırırsanız orijinal Throwable'ı döndürür.future.get()ExecutionExceptiongetCause()

Birkaç (ilgili) puan daha:

  • İstediğiniz görev submitbir sonuç döndürmeyi gerektirmiyor olsa bile , Callable<Void>(a kullanmak yerine Runnable) kullanabilirsiniz.
  • Görevlerin iptali kesme mekanizması kullanılarak yapılabilir . İptal politikasının nasıl uygulanacağına ilişkin bir örnek

Özetle, a submitile Callable( executea ile karşılaştırıldığında Runnable) kullanmak daha iyi bir uygulamadır . "Uygulamada Java eşzamanlılığı" ndan alıntı yapacağım Brian Goetz:

6.3.2 Sonuç doğuran görevler: Çağrılabilir ve Gelecek

Executor çerçevesi temel görev temsili olarak Runnable kullanır. Runnable oldukça sınırlayıcı bir soyutlamadır; run, bir günlük dosyasına yazma veya bir paylaşılan veri yapısına sonuç yerleştirme gibi yan etkilere sahip olabilmesine rağmen, değer döndüremiyor veya işaretli özel durumlar atamıyor. Birçok görev etkin bir şekilde ertelenen hesaplamalardır — bir veritabanı sorgusu yürütme, ağ üzerinden bir kaynak alma veya karmaşık bir işlevi hesaplama. Bu tür görevler için Callable daha iyi bir soyutlamadır: ana giriş noktasının, çağrının bir değer döndürmesini bekler ve bir istisna oluşturabileceğini tahmin eder.7 Yürütücüler, Runnable dahil olmak üzere diğer görev türlerini kaydırmak için çeşitli yardımcı yöntemler içerir ve java.security.PrivilegedAction, bir Callable ile.


1

Sadece kabul edilen cevaba ekleyerek-

Ancak, görevlerden atılan özel durumlar, yalnızca execute () ile gönderilen görevler için yakalanmamış özel durum işleyicisine yapar; execution hizmetine send () ile gönderilen görevler için, atılan istisnalar görevin geri dönüş durumunun bir parçası olarak kabul edilir.

Kaynak

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.