जावा में थ्रेड को ठीक से कैसे रोकें?


276

मुझे जावा में धागे को ठीक से बंद करने के लिए एक समाधान की आवश्यकता है।

मेरे पास IndexProcessorक्लास है जो रननेबल इंटरफ़ेस को लागू करता है:

public class IndexProcessor implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);

    @Override
    public void run() {
        boolean run = true;
        while (run) {
            try {
                LOGGER.debug("Sleeping...");
                Thread.sleep((long) 15000);

                LOGGER.debug("Processing");
            } catch (InterruptedException e) {
                LOGGER.error("Exception", e);
                run = false;
            }
        }

    }
}

और मेरे पास ServletContextListenerकक्षा है जो थ्रेड शुरू और रोकती है:

public class SearchEngineContextListener implements ServletContextListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);

    private Thread thread = null;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        thread = new Thread(new IndexProcessor());
        LOGGER.debug("Starting thread: " + thread);
        thread.start();
        LOGGER.debug("Background process successfully started.");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        LOGGER.debug("Stopping thread: " + thread);
        if (thread != null) {
            thread.interrupt();
            LOGGER.debug("Thread successfully stopped.");
        }
    }
}

लेकिन जब मैं टॉमकैट बंद करता हूं, मुझे अपने IndexProcessor वर्ग में अपवाद मिलता है:

2012-06-09 17:04:50,671 [Thread-3] ERROR  IndexProcessor Exception
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at lt.ccl.searchengine.processor.IndexProcessor.run(IndexProcessor.java:22)
    at java.lang.Thread.run(Unknown Source)

मैं JDK 1.6 का उपयोग कर रहा हूं। तो सवाल यह है:

मैं थ्रेड को कैसे रोक सकता हूं और कोई अपवाद नहीं फेंक सकता हूं?

PS मैं .stop();विधि का उपयोग नहीं करना चाहता क्योंकि यह पदावनत है।


1
एक धागा आधा रास्ता समाप्त हमेशा एक अपवाद उत्पन्न करेगा। यदि यह सामान्य व्यवहार है, तो आप बस पकड़ सकते हैं और इसे अनदेखा कर सकते हैं InterruptedException। यह वही है जो मुझे लगता है, लेकिन मुझे यह भी आश्चर्य है कि मानक तरीका कैसा है।
नागदह

मैं अक्सर थ्रेड्स का उपयोग नहीं कर रहा हूं इसलिए मैं थ्रेड्स में बहुत नया हूं, इसलिए मुझे नहीं पता कि अपवाद को अनदेखा करना सामान्य व्यवहार है या नहीं। इसीलिए पूछ रहा हूं।
पॉलियस मेटुलियोनिस

कई मामलों में अपवाद को अनदेखा करना और विधि प्रसंस्करण को समाप्त करना सामान्य व्यवहार है। नीचे दिए गए मेरे उत्तर को देखें कि यह ध्वज आधारित दृष्टिकोण की तुलना में क्यों सही है।
मैट

1
बी। गोएट्ज़ द्वारा एक साफ-सुथरा स्पष्टीकरण ibm.com/developerworks/library/j-jtp05236InterruptedException पर पाया जा सकता है ।
डेनियल

InterruptedException कोई समस्या नहीं है, पोस्ट किए गए कोड में आपका एकमात्र मुद्दा यह है कि आप इसे एक त्रुटि के रूप में लॉग इन नहीं करना चाहिए, वास्तव में इसे लॉग इन करने के लिए एक सम्मोहक कारण नहीं है, केवल डिबग के अलावा इसे प्रदर्शित करने के लिए कि आपकी रुचि है। । चयनित उत्तर दुर्भाग्यपूर्ण है क्योंकि यह नींद और प्रतीक्षा जैसी छोटी कॉल को काटने की अनुमति नहीं देता है।
नाथन ह्यूजेस

जवाबों:


173

में IndexProcessorवर्ग आप एक ध्वज जो धागा है कि यह, समाप्त करने के लिए चर के लिए इसी तरह की आवश्यकता होगी सूचित स्थापित करने का कोई तरीका होना चाहिए runकि आप सिर्फ वर्ग दायरे में इस्तेमाल किया है।

जब आप थ्रेड को रोकना चाहते हैं, तो आप इस ध्वज को सेट करते हैं और join()थ्रेड पर कॉल करते हैं और इसके समाप्त होने की प्रतीक्षा करते हैं।

सुनिश्चित करें कि एक अस्थिर चर का उपयोग करके या गेट्टर और सेटर विधियों का उपयोग करके ध्वज सुरक्षित है जो ध्वज के रूप में उपयोग किए जा रहे चर के साथ सिंक्रनाइज़ हैं।

public class IndexProcessor implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
    private volatile boolean running = true;

    public void terminate() {
        running = false;
    }

    @Override
    public void run() {
        while (running) {
            try {
                LOGGER.debug("Sleeping...");
                Thread.sleep((long) 15000);

                LOGGER.debug("Processing");
            } catch (InterruptedException e) {
                LOGGER.error("Exception", e);
                running = false;
            }
        }

    }
}

फिर में SearchEngineContextListener:

public class SearchEngineContextListener implements ServletContextListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);

    private Thread thread = null;
    private IndexProcessor runnable = null;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        runnable = new IndexProcessor();
        thread = new Thread(runnable);
        LOGGER.debug("Starting thread: " + thread);
        thread.start();
        LOGGER.debug("Background process successfully started.");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        LOGGER.debug("Stopping thread: " + thread);
        if (thread != null) {
            runnable.terminate();
            thread.join();
            LOGGER.debug("Thread successfully stopped.");
        }
    }
}

3
मैंने ठीक वैसा ही किया है जैसा आपने अपने उत्तर में उदाहरण दिया है इससे पहले कि मैंने देखा है कि आपने इसे संपादित किया है। बहुत बढ़िया जवाब! धन्यवाद, अब सब कुछ पूरी तरह से काम करता है :)
पॉलियस मैटुलियोनिस

1
क्या होगा यदि थ्रेड लॉजिक जटिल है और अन्य वर्गों के बहुत सारे तरीकों का आह्वान करता है? हर जगह बूलियन झंडे की जांच करना संभव नहीं है। फिर क्या करें?
सोटरिक

आपको कोड डिज़ाइन को बदलना होगा ताकि आप इसे इस तरह से बनाएँ जिसमें रननेबल के लिए एक सिग्नल थ्रेड से बाहर निकलने का कारण होगा। अधिकांश उपयोगों में रन विधि में यह लूप होता है इसलिए आमतौर पर समस्या नहीं होती है।
14

3
यदि जुड़ने () स्टेटमेंट में रुकावट आती है तो क्या होता है?
बेनजीता

14
बुरी सलाह फैलाने के लिए अपमानित किया गया। हैंड-रोलेड फ्लैग एप्रोच का मतलब है कि एप्लिकेशन को नींद पूरी होने तक इंतजार करना होगा, जहां रुकावट से नींद कम आएगी। थ्रेड # इंटरप्ट का उपयोग करने के लिए इसे संशोधित करना आसान होगा।
नाथन ह्यूजेस

298

प्रयोग Thread.interrupt()यह करने का एक पूर्ण स्वीकार्य तरीका है। वास्तव में, यह संभवतः एक झंडे के लिए बेहतर है जैसा कि ऊपर सुझाया गया है। इसका कारण यह है कि यदि आप एक अवरोधक अवरोधक कॉल (जैसे Thread.sleepjava.nio चैनल संचालन) का उपयोग कर रहे हैं, तो आप वास्तव में उन लोगों से अलग हो पाएंगे।

यदि आप एक ध्वज का उपयोग करते हैं, तो आपको ब्लॉकिंग ऑपरेशन के समाप्त होने की प्रतीक्षा करनी होगी और फिर आप अपने ध्वज की जांच कर सकते हैं। कुछ मामलों में आपको इसे वैसे भी करना होगा, जैसे कि मानक का उपयोग करना InputStream/ OutputStreamजो रुकावट नहीं है।

उस स्थिति में, जब कोई थ्रेड बाधित होता है, तो यह आईओ को बाधित नहीं करेगा, हालांकि, आप आसानी से अपने कोड में इसे नियमित रूप से कर सकते हैं (और आपको यह रणनीतिक बिंदुओं पर करना चाहिए, जहां आप सुरक्षित रूप से रोक सकते हैं और सफाई कर सकते हैं)

if (Thread.currentThread().isInterrupted()) {
  // cleanup and stop execution
  // for example a break in a loop
}

जैसा कि मैंने कहा, इसका मुख्य लाभ यह Thread.interrupt()है कि आप तुरंत रुकावटों से बाहर निकल सकते हैं, जिसे आप ध्वज दृष्टिकोण के साथ नहीं कर सकते।


32
+1 - Thread.interupt () एड-हॉक फ़्लैग का उपयोग करके एक ही चीज़ को लागू करने के लिए निश्चित रूप से बेहतर है।
स्टीफन C

2
मुझे भी लगता है कि ऐसा करने का यह सही और कुशल तरीका है। +1
रोबोएलेक्स

4
कोड में एक छोटा टाइपो है, Thread.currentThread () में कोष्ठक नहीं है।
व्लाद वी

1
वास्तव में यह ध्वज का उपयोग करने के लिए बेहतर नहीं है क्योंकि जो व्यक्ति धागे के संपर्क में आता है वह कहीं और से इस पर रुकावट डाल सकता है, जिससे यह रुक जाता है और डीबग करना बहुत कठिन होता है। हमेशा एक ध्वज का भी उपयोग करें।
जॉनीटेक्स

इस विशिष्ट मामले में कॉलिंग interrupt()ठीक हो सकती है, लेकिन कई अन्य मामलों में ऐसा नहीं है (उदाहरण के लिए यदि संसाधन को बंद करने की आवश्यकता है)। यदि कोई भी लूप के आंतरिक कामकाज को बदलता है, तो आपको interrupt()बूलियन तरीके से बदलने के लिए याद रखना होगा । मैं शुरुआत से ही सुरक्षित रास्ते पर चलूंगा और झंडे का इस्तेमाल करूंगा।
m0skit0

25

सरल उत्तर: आप दो सामान्य तरीकों में से एक में एक सूत्र को रोक सकते हैं:

  • रन विधि रिटर्न सबरूटिन को हिट करती है।
  • विधि समाप्त करें, और निहित रूप से चलाएँ।

तुम भी धागे को रोक सकते हैं:

  • कॉल करें system.exit(यह आपकी पूरी प्रक्रिया को मारता है)
  • थ्रेड ऑब्जेक्ट की interrupt()विधि को कॉल करें *
  • देखें कि क्या थ्रेड में एक कार्यान्वित विधि है जो लगता है कि यह काम करेगा (जैसे kill()या stop())

*: उम्मीद यह है कि यह एक धागा को रोकने के लिए माना जाता है। हालाँकि, थ्रेड वास्तव में क्या करता है जब ऐसा होता है, तो यह पूरी तरह से उसी चीज़ पर निर्भर करता है जब डेवलपर ने थ्रेड कार्यान्वयन बनाया।

एक सामान्य पैटर्न जिसे आप रन मेथड इम्प्लीमेंटेशन के साथ देखते हैं while(boolean){}, एक है , जहां बूलियन आमतौर पर कुछ नाम दिया गया है isRunning, यह उसके थ्रेड क्लास का एक सदस्य चर है, यह अस्थिर है, और आमतौर पर अन्य थ्रेड्स द्वारा सॉर्ट विधि के द्वारा सुलभ है, जैसे kill() { isRunnable=false; }। ये सबरूटीन्स अच्छे हैं क्योंकि वे किसी भी संसाधन को समाप्त करने से पहले इसे जारी करने की अनुमति देते हैं।


3
"ये सबरूटीन अच्छे हैं क्योंकि वे किसी भी संसाधन को समाप्त करने से पहले इसे जारी करने की अनुमति देते हैं।" मुझे समझ नहीं आ रहा है। आप "आधिकारिक" बाधित स्थिति का उपयोग करके थ्रेड के आयोजित संसाधनों को अच्छी तरह से साफ कर सकते हैं। बस इसे देखें। Thread.currentThread () .Interrupted () या Thread.interrupted () (जो भी आपकी आवश्यकता को पूरा करता है), या InterruptedException, और सफाई को पकड़कर। समस्या कहाँ है?
फ्रांज डी।

मैं यह नहीं समझ पा रहा था कि झंडा विधि क्यों काम करती है, क्योंकि मैं नहीं समझ पाया था कि जब रन हिट होता है तो वह रुक जाता है !!! यह इतना आसान था, प्रिय महोदय आपको इसे इंगित करने के लिए धन्यवाद, किसी ने भी यह स्पष्ट रूप से नहीं किया था।
तहरग्रे

9

आपको हमेशा run()लूप में एक ध्वज (यदि कोई हो) की जांच करके थ्रेड्स को समाप्त करना चाहिए ।

आपका धागा इस तरह दिखना चाहिए:

public class IndexProcessor implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
    private volatile boolean execute;

    @Override
    public void run() {
        this.execute = true;
        while (this.execute) {
            try {
                LOGGER.debug("Sleeping...");
                Thread.sleep((long) 15000);

                LOGGER.debug("Processing");
            } catch (InterruptedException e) {
                LOGGER.error("Exception", e);
                this.execute = false;
            }
        }
    }

    public void stopExecuting() {
        this.execute = false;
    }
}

फिर आप कॉल करके धागे को समाप्त कर सकते हैं thread.stopExecuting()। इस तरह से धागा साफ हो जाता है, लेकिन इसमें 15 सेकंड (आपकी नींद के कारण) लगते हैं। आप अभी भी thread.interrupt () को कॉल कर सकते हैं, अगर यह वास्तव में जरूरी है - लेकिन पसंदीदा तरीका हमेशा ध्वज की जांच करना चाहिए।

15 सेकंड के लिए प्रतीक्षा करने से बचने के लिए, आप नींद को इस तरह विभाजित कर सकते हैं:

        ...
        try {
            LOGGER.debug("Sleeping...");
            for (int i = 0; (i < 150) && this.execute; i++) {
                Thread.sleep((long) 100);
            }

            LOGGER.debug("Processing");
        } catch (InterruptedException e) {
        ...

2
यह एक नहीं है Thread- यह लागू होता है Runnable- आप Threadउस पर तरीके नहीं बुला सकते हैं जब तक कि आप इसे उस स्थिति में घोषित नहीं करते हैं Threadजिस स्थिति में आप कॉल नहीं कर सकते हैंstopExecuting()
डॉन चेडल

7

आमतौर पर, एक थ्रेड समाप्त हो जाता है जब यह बाधित होता है। तो, देशी बूलियन का उपयोग क्यों न करें? आजमाया हुआ है ():

Thread t = new Thread(new Runnable(){
        @Override
        public void run() {
            while(!Thread.currentThread().isInterrupted()){
                // do stuff         
            }   
        }});
    t.start();

    // Sleep a second, and then interrupt
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {}
    t.interrupt();

रेफरी- मैं एक धागे को कैसे मार सकता हूं? स्टॉप () का उपयोग किए बिना;


5

थ्रेड्स को सिंक्रनाइज़ करने के लिए मैं उपयोग करना पसंद CountDownLatchकरता हूं जो थ्रेड्स को प्रतीक्षा करने में मदद करता है जब तक कि प्रक्रिया पूरी नहीं हो जाती। इस मामले में, श्रमिक वर्ग CountDownLatchएक दिए गए गणना के साथ एक उदाहरण के साथ स्थापित है । awaitपद्धति के लिए कॉल तब तक ब्लॉक रहेगी जब तक कि countDownविधि के इनवोकेशन के कारण वर्तमान गणना शून्य तक नहीं पहुंच जाती है या टाइमआउट सेट नहीं हो जाता है। यह दृष्टिकोण थोपने के लिए निर्दिष्ट प्रतीक्षा समय की प्रतीक्षा किए बिना तुरंत एक थ्रेड को बाधित करने की अनुमति देता है:

public class IndexProcessor implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);

    private final CountDownLatch countdownlatch;
    public IndexProcessor(CountDownLatch countdownlatch) {
        this.countdownlatch = countdownlatch;
    }


    public void run() {
        try {
            while (!countdownlatch.await(15000, TimeUnit.MILLISECONDS)) {
                LOGGER.debug("Processing...");
            }
        } catch (InterruptedException e) {
            LOGGER.error("Exception", e);
            run = false;
        }

    }
}

जब आप अन्य थ्रेड का निष्पादन समाप्त करना चाहते हैं, तो काउंटडाउन पर CountDownLatchऔर joinथ्रेड को मुख्य थ्रेड पर निष्पादित करें :

public class SearchEngineContextListener implements ServletContextListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);

    private Thread thread = null;
    private IndexProcessor runnable = null;
    private CountDownLatch countdownLatch = null;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        countdownLatch = new CountDownLatch(1);
        Thread thread = new Thread(new IndexProcessor(countdownLatch));
        LOGGER.debug("Starting thread: " + thread);
        thread.start();
        LOGGER.debug("Background process successfully started.");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        LOGGER.debug("Stopping thread: " + thread);
        if (countdownLatch != null) 
        {
            countdownLatch.countDown();
        } 
        if (thread != null) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                LOGGER.error("Exception", e);
            }
            LOGGER.debug("Thread successfully stopped.");
        } 
    }
}

3

कुछ पूरक जानकारी। जावा डॉक में ध्वज और अवरोध दोनों का सुझाव दिया गया है।

https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

private volatile Thread blinker;

public void stop() {
    blinker = null;
}

public void run() {
    Thread thisThread = Thread.currentThread();
    while (blinker == thisThread) {
        try {
            Thread.sleep(interval);
        } catch (InterruptedException e){
        }
        repaint();
    }
}

लंबे समय तक प्रतीक्षा करने वाले धागे के लिए (उदाहरण के लिए, इनपुट के लिए), उपयोग करें Thread.interrupt

public void stop() {
     Thread moribund = waiter;
      waiter = null;
      moribund.interrupt();
 }

3
कभी भी एक व्यवधान को अनदेखा न करें। इसका मतलब है कि कुछ अन्य कोड स्पष्ट रूप से आपके धागे को समाप्त करने के लिए कह रहे हैं। एक धागा जो उस अनुरोध को अनदेखा करता है वह एक दुष्ट धागा है। एक InterruptedException को हैंडल करने का सही तरीका लूप से बाहर निकलना है।
वीजीआर

2

मुझे एंड्रॉइड में काम करने के लिए रुकावट नहीं मिली, इसलिए मैंने इस पद्धति का उपयोग किया, पूरी तरह से काम करता है:

boolean shouldCheckUpdates = true;

private void startupCheckForUpdatesEveryFewSeconds() {
    threadCheckChat = new Thread(new CheckUpdates());
    threadCheckChat.start();
}

private class CheckUpdates implements Runnable{
    public void run() {
        while (shouldCheckUpdates){
            System.out.println("Do your thing here");
        }
    }
}

 public void stop(){
        shouldCheckUpdates = false;
 }

यह विफल होने की संभावना है, क्योंकि shouldCheckUpdates नहीं है volatileDocs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3 देखें ।
वीजीआर

0

कभी-कभी मैं अपने onDestroy () / ReferenceDestroyed () में 1000 बार कोशिश करूंगा

      @Override
    protected void onDestroy() {
        boolean retry = true;
        int counter = 0;
        while(retry && counter<1000)
        {
            counter++;
            try{thread.setRunnung(false);
                thread.join();
                retry = false;
                thread = null; //garbage can coll
            }catch(InterruptedException e){e.printStackTrace();}
        }

    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.