현재 스레드를 사용하는 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.