क्या कोई ExecutorService है जो वर्तमान थ्रेड का उपयोग करता है?


94

थ्रेड पूल के उपयोग को कॉन्फ़िगर करने के लिए एक संगत तरीका है या नहीं, इसके बाद मैं क्या हूं। आदर्श रूप से बाकी कोड बिल्कुल भी प्रभावित नहीं होना चाहिए। मैं 1 थ्रेड के साथ एक थ्रेड पूल का उपयोग कर सकता हूं लेकिन यह वह नहीं है जो मैं चाहता हूं। कोई विचार?

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

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

जवाबों:


69

यहां वास्तव में सरल है Executor(आप नहीं ExecutorService, मन) कार्यान्वयन जो केवल वर्तमान धागे का उपयोग करता है। इसे "जावा कॉन्सैरेसी इन प्रैक्टिस" (आवश्यक पढ़ना) से चोरी करना।

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

ExecutorService एक अधिक विस्तृत इंटरफ़ेस है, लेकिन उसी दृष्टिकोण के साथ संभाला जा सकता है।


4
+1: जैसा कि आप कहते हैं, एक ExecutorService को उसी तरह से संभाला जा सकता है, शायद AbstractExecutorService को उप-वर्ग करके।
पॉल नागर

@Paul Yep, AbstractExecutorServiceजाने का रास्ता दिखता है।
overthink

15
Java8 में आप इसे कम कर सकते हैंRunnable::run
जॉन फ्रीडमैन

@ जूड्यू यह हमेशा उस थ्रेड पर चलेगा जो एक्ज़ीक्यूटर को कॉल करता है।
गुस्ताव कार्लसन

क्या एक ही-थ्रेड निष्पादक की बात नहीं है, ताकि निष्पादन () के भीतर से अधिक कार्यों को शेड्यूल किया जा सके? यह जवाब नहीं होगा। मुझे ऐसा उत्तर नहीं मिल रहा है जो इसे संतुष्ट करता हो।
१०:१६ पर हैलिक्स २

82

आप अमरूद का उपयोग कर सकते हैं MoreExecutors.newDirectExecutorService(), या MoreExecutors.directExecutor()यदि आपको इसकी आवश्यकता नहीं है ExecutorService

यदि अमरूद बहुत भारी-भरकम है, तो आप कुछ अच्छा कर सकते हैं:

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 (उदाहरण) लौटाता है;
Maragues

यदि हम सभी वर्तमान धागे का उपयोग करते हैं , तो सिंक्रोनाइज़ेशन प्राइमेटीस क्यों? क्यों कुंडी?
हैलिक्स

@ लिलिक्स कुंडी की जरूरत है क्योंकि भले ही काम उसी धागे में किया जाता है, जो काम को जोड़ता है, कोई भी धागा निष्पादक को बंद कर सकता है।
at:

64

जावा 8 शैली:

Executor e = Runnable::run;


7
बिल्कुल गंदी। मुझे यह पसंद है।
दुष्ट

इसके बारे में क्या गंदा है? यह सुरुचिपूर्ण है :)
lpandzic

यह गंदी @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();
    }
}

समाप्त फ़ील्ड को सिंक्रनाइज़ के साथ संरक्षित नहीं किया गया है।
डैनियल एस। यित्सकोव

1
@ DaneelS.Yaitskov terminatedफ़ील्ड कोड के आधार पर सिंक्रनाइज़ एक्सेस से लाभान्वित नहीं होगा जो वास्तव में यहाँ है। 32-बिट क्षेत्रों पर संचालन जावा में परमाणु हैं।
क्रिस्टोफर शुल्त्स

मुझे लगता है कि ऊपर में isTerminated () विधि काफी सही नहीं है क्योंकि isTerminated () केवल वर्तमान में क्रियान्वित कार्यों नहीं कर रहे हैं, तो वापस लौटने के लिए माना जाता है। अमरूद दूसरे चर में कार्यों की संख्या को ट्रैक करता है, जो संभवतः इसलिए कि वे दोनों चर को एक ताला से बचाते हैं।
जेरेमी के

7

आप कार्य को मौजूदा थ्रेड में चलाने के लिए RejectExecutionHandler का उपयोग कर सकते हैं।

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 है, हर कार्य अस्वीकार कर दिया गया है। हालाँकि अस्वीकृत व्यवहार वर्तमान थ्रेड में चलना है। केवल एक समस्या होगी यदि कार्य अस्वीकार नहीं किया गया है।
पीटर लॉरी

8
ध्यान दें, इस नीति का कार्यान्वयन पहले से ही है, अपने स्वयं को परिभाषित करने की कोई आवश्यकता नहीं है java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
jtahlborn

7
यह अधिकतम संभव नहीं है। थ्रेडपूल एक्सक्यूटर बनाने के लिए अधिकतम पूल आकार 0. के साथ। मुझे लगता है कि यह आकार 0 के एक अवरोधन का उपयोग करके व्यवहार को पुन: पेश करना संभव होगा, लेकिन कोई डिफ़ॉल्ट कार्यान्वयन ऐसा करने की अनुमति नहीं देता है।
एक्सल ज़िग्लर

{कोड} के कारण अगर (corePoolSize <0 || maximumPoolSize <= 0 || maximumPoolSize <corePoolSize || KeepAliveTime <0) {कोड} java.util.ThreadPoolExecutor में (कम से कम OpenJDK 7 पर) संकलन नहीं होगा कि
Bogdan

6

मुझे परीक्षण प्रयोजनों के लिए उसी "CurrentThreadExecutorService" का उपयोग करना था और, हालांकि सभी सुझाए गए समाधान अच्छे थे (विशेष रूप से एक अमरूद रास्ते का उल्लेख करते हुए ), मैं पीटर लॉरी द्वारा यहां सुझाए गए समान के साथ आया था ।

जैसा कि यहां एक्सल ज़िग्लर ने उल्लेख किया है , दुर्भाग्य से पीटर का समाधान वास्तव 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);
        }
    };
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.