Mevcut iş parçacığını kullanan bir ExecutorService var mı?


94

Peşinde olduğum şey, bir iş parçacığı havuzunun kullanımını yapılandırmak için uyumlu bir yol. İdeal olarak, kodun geri kalanı hiç etkilenmemelidir. 1 iş parçacığı olan bir iş parçacığı havuzu kullanabilirim ama istediğim tam olarak bu değil. Herhangi bir fikir?

ExecutorService es = threads == 0 ? new CurrentThreadExecutor() : Executors.newThreadPoolExecutor(threads);

// es.execute / es.submit / new ExecutorCompletionService(es) etc

Yanıtlar:


70

Burada , yalnızca mevcut iş parçacığını kullanan gerçekten basit Executor( ExecutorServiceunutmayın) bir uygulama var. Bunu "Uygulamada Java Eşzamanlılığı" ndan çalmak (temel okuma).

public class CurrentThreadExecutor implements Executor {
    public void execute(Runnable r) {
        r.run();
    }
}

ExecutorService daha ayrıntılı bir arayüzdür, ancak aynı yaklaşımla ele alınabilir.


4
+1: Sizin de söylediğiniz gibi, bir ExecutorService de aynı şekilde, belki AbstractExecutorService'i alt sınıflayarak ele alınabilir.
Paul Cager

@Paul Yep, AbstractExecutorServicegidilecek yol gibi görünüyor.
overthink

15
Java8'de bunu sadeceRunnable::run
Jon Freedman

@ Juude, her zaman yürütücüyü çağıran iş parçacığı üzerinde çalışır.
Gustav Karlsson

Aynı iş parçacığı yürütücünün amacı, execute () içinden daha fazla görev zamanlayabilmek değil mi? Bu cevap işe yaramayacak. Bunu tatmin edecek bir cevap bulamıyorum.
haelix

83

Guava'yı kullanabilirsiniz MoreExecutors.newDirectExecutorService()veya MoreExecutors.directExecutor()ihtiyacınız yoksa ExecutorService.

Guava'yı dahil etmek çok ağırsa, neredeyse aynı derecede iyi bir şey uygulayabilirsiniz:

public final class SameThreadExecutorService extends ThreadPoolExecutor {
  private final CountDownLatch signal = new CountDownLatch(1);

  private SameThreadExecutorService() {
    super(1, 1, 0, TimeUnit.DAYS, new SynchronousQueue<Runnable>(),
        new ThreadPoolExecutor.CallerRunsPolicy());
  }

  @Override public void shutdown() {
    super.shutdown();
    signal.countDown();
  }

  public static ExecutorService getInstance() {
    return SingletonHolder.instance;
  }

  private static class SingletonHolder {
    static ExecutorService instance = createInstance();    
  }

  private static ExecutorService createInstance() {
    final SameThreadExecutorService instance
        = new SameThreadExecutorService();

    // The executor has one worker thread. Give it a Runnable that waits
    // until the executor service is shut down.
    // All other submitted tasks will use the RejectedExecutionHandler
    // which runs tasks using the  caller's thread.
    instance.submit(new Runnable() {
        @Override public void run() {
          boolean interrupted = false;
          try {
            while (true) {
              try {
                instance.signal.await();
                break;
              } catch (InterruptedException e) {
                interrupted = true;
              }
            }
          } finally {
            if (interrupted) {
              Thread.currentThread().interrupt();
            }
          }
        }});
    return Executors.unconfigurableScheduledExecutorService(instance);
  }
}

1
Android için, Executors.unconfigurableExecutorService (örnek) döndürür;
2018, 09:45

tek kullandığımız mevcut iş parçacığı ise , neden senkronizasyon ilkelleri? neden mandal?
haelix

@haelix mandalı gereklidir, çünkü iş, işi ekleyen iş parçacığı ile aynı iş parçacığında yapılsa bile, herhangi bir iş parçacığı yürütücüyü kapatabilir.
NamshubWriter

63

Java 8 stili:

Executor e = Runnable::run;


9
Kesinlikle pis. Onu seviyorum.
Rogue

Bunun nesi pis? Zarif :)
lpandzic

1
En pis @Ipandzic türü, sıradışı ve özlü.
Rogue

12

Bir ExecutorServicedayanarak yazdım AbstractExecutorService.

/**
 * Executes all submitted tasks directly in the same thread as the caller.
 */
public class SameThreadExecutorService extends AbstractExecutorService {

    //volatile because can be viewed by other threads
    private volatile boolean terminated;

    @Override
    public void shutdown() {
        terminated = true;
    }

    @Override
    public boolean isShutdown() {
        return terminated;
    }

    @Override
    public boolean isTerminated() {
        return terminated;
    }

    @Override
    public boolean awaitTermination(long theTimeout, TimeUnit theUnit) throws InterruptedException {
        shutdown(); // TODO ok to call shutdown? what if the client never called shutdown???
        return terminated;
    }

    @Override
    public List<Runnable> shutdownNow() {
        return Collections.emptyList();
    }

    @Override
    public void execute(Runnable theCommand) {
        theCommand.run();
    }
}

sonlandırılan alan senkronize ile korunmaz.
Daneel Yaitskov

1
@ DaneelS.Yaitskov terminatedalanı, gerçekte burada bulunan koda dayalı senkronize erişimden yararlanmayacaktır. 32 bit alanlardaki işlemler Java'da atomiktir.
Christopher Schultz

Sanırım yukarıdaki isTerminated () yöntemi pek doğru değil çünkü isTerminated () yalnızca şu anda yürütülen hiçbir görev yoksa true döndürmesi gerekiyor. Guava, başka bir değişkendeki görevlerin sayısını izler, muhtemelen bu yüzden her iki değişkeni de bir kilitle korurlar.
Jeremy K

7

Görevi geçerli iş parçacığı içinde çalıştırmak için RejectedExecutionHandler öğesini kullanabilirsiniz.

public static final ThreadPoolExecutor CURRENT_THREAD_EXECUTOR = new ThreadPoolExecutor(0, 0, 0, TimeUnit.DAYS, new SynchronousQueue<Runnable>(), new RejectedExecutionHandler() {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        r.run();
    }
});

Şimdiye kadar bunlardan sadece birine ihtiyacınız var.


Zekice! Bu ne kadar güvenli (dürüst soru)? Mevcut iş parçacığında gerçekten yürütmek istemediğiniz bir görevin reddedilmesi için herhangi bir yol var mı? ExecutorService kapatılır veya sonlandırılırsa görevler reddedilir mi?
overthink

Maksimum boyut 0 olduğu için her görev reddedilir. Ancak reddedilen davranış, mevcut iş parçacığında çalışmaktır. Yalnızca görev reddedilmezse sorun olur.
Peter Lawrey

8
dikkat edin, bu politikanın zaten bir uygulaması var, kendinizinkini tanımlamanıza gerek yok java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy.
jtahlborn

7
Artık maksimum havuz boyutu 0 olan bir ThreadPoolExecutor oluşturmak mümkün değil. 0 boyutunda bir blockingQueue kullanarak davranışı yeniden oluşturmak mümkün olabilirdi, ancak hiçbir varsayılan uygulama buna izin vermiyor gibi görünüyor.
Axelle Ziegler

java.util.ThreadPoolExecutor'da (en azından openJdk 7)
Bogdan

7

Test amacıyla aynı "CurrentThreadExecutorService" hizmetini kullanmak zorunda kaldım ve önerilen tüm çözümler güzel olmasına rağmen (özellikle Guava yönteminden bahseden ), Peter Lawrey'nin burada önerdiğine benzer bir şey buldum .

Axelle Ziegler tarafından belirtildiği gibi burada maalesef Peter çözüm çünkü tanıtılan çek değil aslında çalışma olacak ThreadPoolExecutorüzerinde maximumPoolSizeYapıcı parametre (yani maximumPoolSizeolamaz <=0).

Bunu aşmak için şunları yaptım:

private static ExecutorService currentThreadExecutorService() {
    CallerRunsPolicy callerRunsPolicy = new ThreadPoolExecutor.CallerRunsPolicy();
    return new ThreadPoolExecutor(0, 1, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), callerRunsPolicy) {
        @Override
        public void execute(Runnable command) {
            callerRunsPolicy.rejectedExecution(command, this);
        }
    };
}
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.