現在のスレッドを使用するExecutorServiceはありますか?


94

私が求めているのは、スレッドプールの使用を構成するための互換性のある方法です。理想的には、残りのコードはまったく影響を受けないようにする必要があります。1スレッドのスレッドプールを使用することもできますが、それは私が望むものではありません。何か案は?

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

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

回答:


69

これは、現在のスレッドのみを使用する本当に単純なExecutor(そうではありませExecutorServiceん)実装です。「Java Concurrency in Practice」からこれを盗み取ります(必読)。

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

ExecutorService はより複雑なインターフェースですが、同じアプローチで処理できます。


4
+1:あなたが言うように、ExecutorServiceは、おそらくAbstractExecutorServiceをサブクラス化することによって、同じ方法で処理できます。
Paul Cager、2011

@ポールうん、AbstractExecutorService行く方法のように見えます。
overthink

14
Java8ではこれを単純に減らすことができますRunnable::run
Jon Freedman、

@Juudeは常にexecutorを呼び出すスレッドで実行されます。
グスタフカールソン2017

execute()内からより多くのタスクをスケジュールできるようにするには、同じスレッドのexecutorのポイントではありませんか?この答えは意味がありません。これを満たす答えが見つからない。
haelix 2018

82

Guavaを使用するMoreExecutors.newDirectExecutorService()MoreExecutors.directExecutor()、必要ない場合はを使用できますExecutorService

Guavaを含めるのが重すぎる場合は、ほぼ同じように実装できます。

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の場合、これはExecutors.unconfigurableExecutorService(instance);を返します。
Maragues

使用しているのが現在のスレッドだけの場合、なぜ同期プリミティブなのでしょうか。なぜラッチ?
haelix 2018

@haelixラッチが必要なのは、作業が作業を追加したスレッドと同じスレッドで実行されている場合でも、どのスレッドでもエグゼキューターをシャットダウンできるためです。
NamshubWriter

64

Java 8スタイル:

Executor e = Runnable::run;


7
絶対に汚い。大好きです。
ローグ、

それについて汚い何ですか?エレガントです:)
lpandzic 2018年

これは、汚い@Ipandzicの最高の種類であり、珍しくて簡潔です。
ローグ、

12

ExecutorService基づいて書いた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();
    }
}

終了したフィールドは同期された状態で保護されていません。
Daneel S. Yaitskov

1
@ DaneelS.Yaitskov terminatedフィールドは、実際にここにあるコードに基づいた同期アクセスの恩恵を受けません。Javaでは、32ビットフィールドの操作はアトミックです。
クリストファーシュルツ2016

上記のisTerminated()メソッドは、現在実行中のタスクがない場合にのみtrueを返すことになっているため、正しくないと思います。Guavaは別の変数でタスクの数を追跡します。これがおそらく両方の変数をロックで保護する理由です。
Jeremy K

6

テストの目的で同じ "CurrentThreadExecutorService"を使用する必要があり、提案されたすべての解決策(特にGuavaの方法に言及しいる解決策)は優れていましたが、Peter Lawreyがここで提案したものに似たものを思いつきまし

ここでAxelle Zieglerが述べたように、残念ながら、PeterのソリューションはThreadPoolExecutormaximumPoolSizeコンストラクターパラメーターで導入されたチェックのために実際には機能しません(つまり、にmaximumPoolSizeすることはできません<=0)。

それを回避するために、私は次のことを行いました:

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

5

RejectedExecutionHandlerを使用して、現在のスレッドでタスクを実行できます。

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

あなたはこれらのうちの1つしか必要としません。


賢い!これはどれくらい安全ですか(正直な質問)?現在のスレッドで実際に実行したくないタスクを拒否する方法はありますか?ExecutorServiceがシャットダウンまたは終了した場合、タスクは拒否されますか?
overthink

最大サイズは0であるため、すべてのタスクが拒否されます。ただし、拒否された動作は現在のスレッドで実行することです。タスクが拒否されない場合にのみ問題があります。
Peter Lawrey、2011

8
このポリシーはすでに実装されているため、独自に定義する必要はありませんjava.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
jtahlborn

7
最大プールサイズが0のThreadPoolExecutorを作成することはできなくなりました。サイズ0のブロッキングキューを使用して動作を再現することは可能だと思いますが、デフォルトの実装ではそれを許可していないようです。
Axelle Ziegler、2011

{core}が原因でコンパイルされない場合(corePoolSize <0 || maximumPoolSize <= 0 || maximumPoolSize <corePoolSize || keepAliveTime <0){code} in java.util.ThreadPoolExecutor(少なくともopenJdk 7)
Bogdan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.