현재 스레드를 사용하는 ExecutorService가 있습니까?


94

내가 추구하는 것은 스레드 풀 사용 여부를 구성하는 호환 가능한 방법입니다. 이상적으로 나머지 코드는 전혀 영향을받지 않아야합니다. 스레드가 1 개인 스레드 풀을 사용할 수 있지만 그게 내가 원하는 것이 아닙니다. 어떤 아이디어?

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

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

답변:


69

여기 정말 간단 Executor(하지 ExecutorService만 현재 스레드를 사용하는 당신을 마음) 구현. "실습에서 Java 동시성"(필수 읽기)에서 이것을 훔칩니다.

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

ExecutorService 보다 정교한 인터페이스이지만 동일한 접근 방식으로 처리 할 수 ​​있습니다.


4
+1 : 당신이 말했듯이 ExecutorService는 아마도 AbstractExecutorService를 서브 클래 싱함으로써 같은 방식으로 처리 될 수 있습니다.
Paul Cager 2011-07-05

@Paul Yep, AbstractExecutorService갈 길처럼 보입니다.
overthink 2011-07-05

14
Java8에서 당신은이를 줄일 수 있습니다Runnable::run
존 프리드먼

@Juude 항상 실행자를 호출하는 스레드에서 실행됩니다.
Gustav Karlsson

execute () 내에서 더 많은 작업을 예약 할 수있는 동일한 스레드 실행자의 요점이 아닌가요? 이 대답은 그렇지 않습니다. 이것에 맞는 답을 찾을 수 없습니다.
haelix dec

82

당신은 구아바의를 사용할 수 있습니다 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의 경우 return Executors.unconfigurableExecutorService (instance);
Maragues

우리가 사용하는 전부가 현재 쓰레드 라면, 왜 동기화 프리미티브일까요? 왜 래치?
haelix

@haelix 작업을 추가 한 스레드와 동일한 스레드에서 작업을 수행하더라도 모든 스레드가 실행기를 종료 할 수 있기 때문에 래치가 필요합니다.
NamshubWriter

64

자바 8 스타일 :

Executor e = Runnable::run;


7
절대적으로 더럽습니다. 나는 그것을 좋아한다.
Rogue

그것에 대해 더러워진 것은 무엇입니까? 그것은 우아한 :)입니다
lpandzic

@Ipandzic 최고의 더러운 것입니다. 독특하고 간결합니다.
Rogue

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필드는 실제로 여기에있는 코드를 기반으로 한 동기화 된 액세스의 이점을 얻지 못합니다. 32 비트 필드에 대한 작업은 Java에서 원자 적입니다.
크리스토퍼 슐츠

위의 isTerminated () 메서드는 현재 실행중인 작업이없는 경우에만 isTerminated ()가 true를 반환해야하기 때문에 옳지 않다고 생각합니다. Guava는 다른 변수의 작업 수를 추적하므로 두 변수를 모두 잠금으로 보호합니다.
Jeremy K

6

테스트 목적으로 동일한 "CurrentThreadExecutorService"를 사용해야했으며 제안 된 모든 솔루션이 훌륭했지만 (특히 Guava 방식을 언급 한 솔루션 ) Peter Lawrey가 여기서 제안한 것과 유사한 것을 생각해 냈습니다 .

Axelle Ziegler가 여기 에서 언급했듯이 , 안타깝게도 Peter의 솔루션은 생성자 매개 변수 ThreadPoolExecutor에 도입 된 검사로 인해 실제로 작동하지 않습니다 maximumPoolSize(예 : 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();
    }
});

이것들 중 하나만 필요합니다.


영리한! 이것이 얼마나 안전합니까 (정직한 질문)? 현재 스레드에서 실제로 실행하고 싶지 않은 작업이 거부되는 방법이 있습니까? ExecutorService가 종료되거나 종료되면 작업이 거부됩니까?
overthink

최대 크기가 0이므로 모든 작업이 거부됩니다. 그러나 거부 된 동작은 현재 스레드에서 실행됩니다. 작업이 거부되지 않은 경우에만 문제가 발생합니다.
Peter Lawrey 2011

8
이미이 정책이 구현되어 있으므로 직접 정의 할 필요가 없습니다 java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy.
jtahlborn 2011-07-05

7
더 이상 최대 풀 크기가 0 인 ThreadPoolExecutor를 만들 수 없습니다. 크기 0의 blockingQueue를 사용하여 동작을 재현 할 수있을 것 같지만 기본 구현은이를 허용하지 않는 것 같습니다.
Axelle Ziegler 2011 년

{code} if (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.