2009-11-27 16 views
5

रद्द करने के तरीके को कैसे संभालें पर सबमिट किए गए कार्यों के निष्पादन को ट्रैक करने के लिए कॉलबैक प्रदान करने के लिए मैंने java.util.concurrent से FutureTask बढ़ाया है।फ्यूचरटास्क का विस्तार,

public class StatusTask<V> extends FutureTask<V> { 

    private final ITaskStatusHandler<V> statusHandler; 

    public StatusTask(Callable<V> callable, ITaskStatusHandler<V> statusHandler){ 
     super(callable); 
     if (statusHandler == null) 
      throw new NullPointerException("statusHandler cannot be null"); 
     this.statusHandler = statusHandler; 
     statusHandler.TaskCreated(this); 
    } 

    @Override 
    public void run() { 
     statusHandler.TaskRunning(this); 
     super.run(); 
    } 

    @Override 
    protected void done() { 
     super.done(); 
     statusHandler.TaskCompleted(this); 
    } 

} 

अब, मैं क्या देखते हैं अगर काम प्रस्तुत किया जाता है, लेकिन पंक्तिबद्ध समाप्त होता है और मैं काम cancel(true); - run() पद्धति अभी भी बुलाया जाता है - और FutureTask.run() (संभावना) की जाँच करता है कि कार्य को रद्द कर दिया और नहीं करता है लपेटा कॉल करने योग्य कॉल नहीं है।

क्या मुझे ऐसा करना चाहिए

@Override 
public void run() { 
    if(!isCancelled()) { 
    statusHandler.TaskRunning(this); 
    super.run(); 
    } 
} 

या मुझे अभी भी super.run() पर कॉल करना चाहिए? इन दोनों दृष्टिकोणों को रद्दीकरण की जांच और इसके बारे में कुछ करने के बीच दौड़ की स्थिति के लिए अतिसंवेदनशील लगता है .. किसी भी विचार की सराहना की।

उत्तर

3

आप वहीं एक दौड़ नहीं है कि कर रहे हैं। FutureTask#done() को पर एक बार पर कॉल किया जाएगा, इसलिए यदि कार्य को पहले से ही RunnableFuture#run() के माध्यम से चलाने से पहले रद्द कर दिया गया है, तो आप FutureTask#done() पर कॉल को चूक गए होंगे।

क्या आपने एक सरल दृष्टिकोण माना है जो हमेशा ITaskStatusHandler#taskRunning() और ITaskStatusHandler#taskCompleted() पर जोड़े गए कॉल का एक सममित सेट जारी करता है?

@Override 
public void run() { 
    statusHandler.TaskRunning(this); 
    try { 
    super.run(); 
    finally { 
    statusHandler.TaskCompleted(this); 
    } 
} 

एक बार RunnableFuture#run() कहा जाता है, यह सच है कि दौड़ में शामिल अपने कार्य, या कम से कम चलाने के लिए कोशिश कर रहा है। एक बार FutureTask#run() किया जाता है, तो आपका कार्य अब नहीं चल रहा है। ऐसा इसलिए होता है कि रद्दीकरण के मामले में, संक्रमण (लगभग) तत्काल है।

ITaskStatusHandler#taskRunning() बुला से बचने की कोशिश करता है, तो भीतरी Callable या RunnableFutureTask#run() द्वारा लाया कभी नहीं किया जाता है, Callable या Runnable और FutureTask व्युत्पन्न प्रकार खुद के बीच कुछ साझा ढांचा स्थापित करने की आवश्यकता होगी ताकि जब अपने भीतर समारोह पहले कहा जाता है आप कुछ ध्वज सेट करते हैं कि बाहरी FutureTask -कृत प्रकार एक लोच के रूप में देख सकता है, यह दर्शाता है कि हां, से पहले रद्द हो गया था। हालांकि, तब तक, आपको ITaskStatusHandler#taskRunning() पर कॉल करने के लिए प्रतिबद्ध होना पड़ा, इसलिए भेद इतना उपयोगी नहीं है।

मैं हाल ही में एक समान डिजाइन समस्या के साथ संघर्ष कर रहा है, और मेरे अधिरोहित FutureTask#run() विधि में आपरेशन के बाद सममित पर निपटाने और से पहले बंद कर।

+0

मुझे लगता है कि अंतिम खंड में कॉल स्थिति होना चाहिए हैंडलर। कार्य पूर्ण (यह); क्या कोई ऐसा मामला है जहां रन विधि नहीं कहा जाएगा लेकिन किया गया() विधि होगी? – nos

+0

गलती को पकड़ने के लिए धन्यवाद। मैंने अंत में ब्लॉक में कॉल तय की। हां, यह संभव है कि किया गया() को रन() के बिना बुलाया जा सकता है। फ्यूचरटास्क # सिंक # आंतरिक कैंसल (बूलियन) देखें। वहां, बशर्ते कि कार्य चलाना समाप्त नहीं हुआ है, जिसमें यह भी शुरू नहीं हो रहा है, आप देख सकते हैं कि किया गया() कहा जाएगा। ध्यान दें कि() को किसी भी भविष्य के लिए शून्य या एक बार कहा जाएगा: शून्य अगर न तो चलाएं() न ही रद्द करें() को कभी-कभी नहीं कहा जाता है। – seh

+0

हालांकि, यदि आप इसे निष्पादित करने वाले निष्पादक को सबमिट करते हैं() को कॉल किया जाएगा, तो उसे निष्पादित करने से पहले कार्य रद्द कर दिया जाएगा - हालांकि निष्पादक को इसके बारे में पता नहीं है। निष्पादक केवल रननेबल/कॉलबेल के बारे में जानता है। तो, रन() को तब कॉल किया जाएगा जब अंततः उस मामले में रननेबल हो जाता है FutureTask # run() अनिवार्य रूप से कुछ भी नहीं करता है। – nos

2

आपकी समस्या यह है कि आपके भविष्य के कार्यों को अभी भी रद्द कर दिया गया है, जिन्हें आपने रद्द कर दिया है, है ना?

निष्पादक सेवा में एक कार्य सबमिट करने के बाद इसे निष्पादक द्वारा प्रबंधित किया जाना चाहिए। (यदि आप चाहें तो भी आप एक ही कार्य को रद्द कर सकते हैं।) आपको executor shutdownNow method के साथ निष्पादन रद्द करना चाहिए। (यह सभी सबमिट किए गए कार्यों की रद्द विधि को कॉल करेगा।) एक शट डाउन अभी भी सभी सबमिट किए गए कार्यों को निष्पादित करेगा।

निष्पादक अन्यथा "नहीं जानता" कि एक कार्य रद्द कर दिया गया था। यह भविष्य के कार्य की आंतरिक स्थिति से स्वतंत्र रूप से विधि को कॉल करेगा।

सबसे आसान तरीका एक्ज़ीक्यूटोर फ्रेमवर्क का उपयोग करना और एक कॉल करने योग्य सजावट लिखना होगा।

class CallableDecorator{ 

    CallableDecorator(Decorated decorated){ 
    ... 
    } 

    setTask(FutureTask task){ 
    statusHandler.taskCreated(task); 
    } 

    void call(){ 
    try{ 
     statusHandler.taskRunning(task); 
     decorated.call(); 
    }finally{ 
     statusHandler.taskCompleted(task); 
    } 
    } 
} 

एकमात्र समस्या यह है कि कार्य सजावट के निर्माता में नहीं हो सकता है। (यह भविष्य के कार्य कन्स्ट्रक्टर का एक पैरामीटर है।) इस चक्र को तोड़ने के लिए आपको कन्स्ट्रक्टर इंजेक्शन के साथ एक सेटर या कुछ प्रॉक्सी काम का उपयोग करना होगा। शायद कॉलबैक के लिए इसकी आवश्यकता नहीं है और आप कह सकते हैं: statusHandler.callableStarted(decorated)

आपकी आवश्यकताओं के आधार पर आपको अपवाद और बाधाओं को सिग्नल करना पड़ सकता है।

बेसिक कार्यान्वयन:

class CallableDecorator<T> implements Callable<T> { 

    private final Callable<T> decorated; 
    CallableDecorator(Callable<T> decorated){ 
     this.decorated = decorated; 
    } 

    @Override public T call() throws Exception { 
     out.println("before " + currentThread()); 
     try { 
      return decorated.call(); 
     }catch(InterruptedException e){ 
      out.println("interupted " + currentThread()); 
      throw e; 
     } 
     finally { 
      out.println("after " + currentThread()); 
     } 
    } 
} 

ExecutorService executor = newFixedThreadPool(1); 
Future<Long> normal = executor.submit(new CallableDecorator<Long>(
     new Callable<Long>() { 
      @Override 
      public Long call() throws Exception { 
       return System.currentTimeMillis(); 
      } 
     })); 
out.println(normal.get()); 

Future<Long> blocking = executor.submit(new CallableDecorator<Long>(
     new Callable<Long>() { 
      @Override 
      public Long call() throws Exception { 
       sleep(MINUTES.toMillis(2)); // blocking call 
       return null; 
      } 
     })); 

sleep(SECONDS.toMillis(1)); 
blocking.cancel(true); // or executor.shutdownNow(); 

आउटपुट:

before Thread[pool-1-thread-1,5,main] 
after Thread[pool-1-thread-1,5,main] 
1259347519104 
before Thread[pool-1-thread-1,5,main] 
interupted Thread[pool-1-thread-1,5,main] 
after Thread[pool-1-thread-1,5,main] 
+2

गंभीरता से, आप पूरे निष्पादक को मार कर एक व्यक्ति को कॉल करने योग्य नियंत्रण करना चाहते हैं? यदि आपके पास प्रति कॉल करने योग्य एक निष्पादक था तो यह बहुत अच्छा काम करेगा। किस मामले में, निष्पादकों का भी उपयोग क्यों करें? आम तौर पर आप चुनिंदा नौकरियों को रद्द करना चाहते हैं, इसलिए भविष्य पर रद्द विधि। – james

+0

नहीं। नमूना कोड दिखाता है कि निष्पादक या एक कार्य को कैसे मारना है। –

संबंधित मुद्दे