কিভাবে জাভায় থ্রেড সঠিকভাবে বন্ধ করবেন?


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.");
        }
    }
}

তবে যখন আমি টমকাট বন্ধ করি, তখন আমি আমার ইনডেক্স প্রসেসর শ্রেণিতে ব্যতিক্রম পাই:

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)

আমি জেডিকে ১.6 ব্যবহার করছি। সুতরাং প্রশ্নটি হ'ল:

আমি কীভাবে থ্রেডটি থামাতে পারি এবং কোনও ব্যতিক্রম ছোঁড়াতে পারি না?

PS আমি .stop();পদ্ধতিটি ব্যবহার করতে চাই না কারণ এটি অবনমিত।


1
একটি থ্রেড অর্ধেক পথ সমাপ্তি সর্বদা একটি ব্যতিক্রম উত্পন্ন করবে। যদি এটি স্বাভাবিক আচরণ হয় তবে আপনি এটিকে কেবল ধরা এবং উপেক্ষা করতে পারেন InterruptedException। এটি আমি যা মনে করি তা হ'ল তবে আমিও আশ্চর্য হয়েছি যে স্ট্যান্ডার্ড উপায়টি কেমন।
নাহধঃ

আমি থ্রেডগুলি প্রায়শই ব্যবহার করি না তাই থ্রেডগুলিতে আমি বেশ নতুন, তাই ব্যতিক্রমটি উপেক্ষা করা স্বাভাবিক আচরণ কিনা তা আমি জানি না। এজন্যই আমি জিজ্ঞাসা করছি।
পলিয়াস ম্যাটুলিওনিস

অনেক ক্ষেত্রে ব্যতিক্রম উপেক্ষা করা এবং পদ্ধতি প্রক্রিয়াজাতকরণ বন্ধ করা স্বাভাবিক আচরণ। এটি পতাকা ভিত্তিক পদ্ধতির চেয়ে কেন বেটার কারণ নীচে আমার উত্তর দেখুন See
ম্যাট

1
বি গোয়েজের সম্পর্কিত একটি ঝরঝরে ব্যাখ্যা আইবিএম . com/ ডেভেলপ ওয়ার্কস / লাইবারি / জ-jtp05236InterruptedException এ পাওয়া যাবে
ড্যানিয়েল

বিঘ্নিত ধারণাটি কোনও সমস্যা নয়, পোস্ট করা কোডটিতে আপনার একমাত্র সমস্যাটি হ'ল এটি একটি ত্রুটি হিসাবে লগ করা উচিত নয়, আপনার আগ্রহের ক্ষেত্রে এটি ঘটেছিল তা প্রদর্শনের জন্য এটি কেবলমাত্র ডিবাগ ছাড়া লগ করার কোনও বাধ্যতামূলক কারণ নেই really । নির্বাচিত উত্তরটি দুর্ভাগ্যজনক কারণ এটি ঘুম এবং অপেক্ষা করার মতো কলগুলিতে সংক্ষিপ্ত কলগুলি কাটতে দেয় না।
নাথান হিউজেস

উত্তর:


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
থ্রেড লজিক যদি জটিল হয় এবং অন্যান্য ক্লাসের প্রচুর পদ্ধতিতে আবেদন করে তবে কী হবে? কোথাও বুলিয়ান পতাকা চেক করা সম্ভব নয়। তাহলে কি করব?
সোটারিক

আপনাকে কোড ডিজাইনের পরিবর্তন করতে হবে যাতে আপনি এটিকে এমনভাবে তৈরি করেন যাতে রান্নেবলের সংকেত থ্রেডটি প্রস্থান করতে পারে। বেশিরভাগ ব্যবহারের রান পদ্ধতিতে এই লুপ থাকে তাই সাধারণত সমস্যা হয় না।
DrYap

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 - অ্যাড-হক পতাকা ব্যবহার করে একই জিনিস বাস্তবায়িত করার জন্য অবশ্যই থ্রেড.ইনটারআপ্ট () অবশ্যই পছন্দনীয়।
স্টিফেন সি

2
আমি এটিও মনে করি যে এটি করার সঠিক এবং দক্ষ উপায়। +1
রোবোক্লেক্স

4
কোডটিতে একটি ছোট টাইপ রয়েছে, থ্রেড.কন্ট্রেন্টথ্রেড () এর প্রথম বন্ধনী নেই।
ভ্লাদ ভি

1
প্রকৃতপক্ষে পতাকা ব্যবহার করা পছন্দনীয় নয় কারণ থ্রেডের সংস্পর্শে আসা অন্য যে কেউ এটি অন্য কোথাও থেকে বাধা দিতে পারে, যার ফলে এটি থামতে পারে এবং ডিবাগ করা খুব কঠিন হয়। সর্বদা একটি পতাকা ব্যবহার করুন।
JohnyTex

এই নির্দিষ্ট ক্ষেত্রে কলিং ঠিকঠাক interrupt()হতে পারে, তবে অন্যান্য অনেক ক্ষেত্রে এটি হয় না (উদাহরণস্বরূপ যদি কোনও সংস্থান বন্ধ করার দরকার হয়)। যদি কেউ লুপটির অভ্যন্তরীণ কার্যকারিতা পরিবর্তন interrupt()করে তবে আপনাকে বুলিয়ান পথে পরিবর্তন করতে হবে। আমি প্রথম থেকেই নিরাপদ পথে যাব এবং পতাকাটি ব্যবহার করব।
m0skit0

25

সাধারণ উত্তর: আপনি দুটি সাধারণ উপায়ে একটির মাধ্যমে স্বতন্ত্রভাবে একটি থ্রেড থামাতে পারেন:

  • রান পদ্ধতিটি একটি রিটার্ন সাববুটিনকে হিট করে।
  • রান পদ্ধতিটি শেষ হয়ে যায় এবং স্পষ্টভাবে ফিরে আসে।

আপনি থ্রেডগুলি একেবারে বন্ধ করতে পারেন:

  • কল করুন system.exit(এটি আপনার সম্পূর্ণ প্রক্রিয়াটিকে হত্যা করে)
  • থ্রেড অবজেক্টের interrupt()পদ্ধতিটিকে কল করুন *
  • থ্রেডটির কোনও কার্যকর পদ্ধতি রয়েছে যা দেখে মনে হচ্ছে এটি কার্যকর হবে (যেমন kill()বা stop())

*: প্রত্যাশাটি এটি একটি থ্রেড থামাতে হবে বলে মনে করা হচ্ছে। যাইহোক, থ্রেডটি আসলে যা ঘটে তা সম্পূর্ণভাবে বিকাশকারীরা থ্রেড বাস্তবায়ন তৈরি করার সময় যা লিখেছিল তা নির্ভর করে।

রান মেথড বাস্তবায়নের সাথে আপনি যে সাধারণ প্যাটার্নটি দেখতে পান তা হ'ল একটি while(boolean){}, যেখানে বুলিয়ান সাধারণত কিছু নামকরণ করা হয় isRunning, এটি এর থ্রেড শ্রেণীর সদস্য পরিবর্তনশীল, এটি উদ্বায়ী এবং সাধারণত সেটারের পদ্ধতি দ্বারা অন্য থ্রেড দ্বারা সাধারণত অ্যাক্সেসযোগ্য kill() { isRunnable=false; }। এই সাবরুটাইনগুলি দুর্দান্ত কারণ তারা থ্রেডটি সমাপ্তির আগে ধারণ করে এমন কোনও সংস্থান মুক্তি দিতে দেয়।


3
"এই সাবরুটাইনগুলি দুর্দান্ত কারণ তারা থ্রেডটি সমাপ্তির আগে ধারণ করা কোনও সংস্থান মুক্তি দিতে দেয়।" আমি বুঝতে পারছি না। আপনি "অফিসিয়াল" বাধার স্থিতি ব্যবহার করে কোনও থ্রেডের সংস্থানিত সংস্থানগুলি পুরোপুরি ভালভাবে পরিষ্কার করতে পারেন। থ্রেড.কন্ট্রেনথ্রেড () .আইসটার্ট্রাপড () বা থ্রেড.ইনটারপ্রেড () যেটি আপনার প্রয়োজন অনুসারে ফিট করে) ব্যবহার করে এটি পরীক্ষা করে দেখুন বা ইন্টারপ্রেডএক্সসেপশন এবং ক্লিনআপটি ধরুন। সমস্যা কোথায়?
ফ্রানজ ডি।

পতাকা পদ্ধতিটি কেন কাজ করে তা আমি বুঝতে সক্ষম হইনি, কারণ আমি বুঝতে পারি নি যে রান হিট হওয়ার পরে এটি থামবে !!! এটি এত সহজ ছিল, প্রিয় স্যার আপনাকে এটিকে নির্দেশ করার জন্য আপনাকে ধন্যবাদ, কেউ এটিকে সুস্পষ্টভাবে করেনি।
থাহাগর

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 সেকেন্ড পর্যন্ত সময় নেয় (আপনার ঘুমের কারণে)। এটি সত্যিই জরুরি হলে আপনি এখনও থ্রেড.ইন্টারট্রপ () কল করতে পারেন - তবে পছন্দসই উপায়টি সর্বদা পতাকাটি পরীক্ষা করা উচিত।

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- আপনি কল করতে না ThreadThreadstopExecuting()
ডন চ্যাডল

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
কোনও বাধাপ্রাপ্তি অবজ্ঞা কখনও উপেক্ষা করবেন না। এর অর্থ অন্য কিছু কোড সুস্পষ্টভাবে আপনার থ্রেডটি বন্ধ করতে বলছে। এমন একটি থ্রেড যা সেই অনুরোধটিকে উপেক্ষা করে তা হ'ল দুর্বৃত্ত থ্রেড। ইন্টারপ্রেড এক্সসেপশন হ্যান্ডেল করার সঠিক উপায়টি হল লুপ থেকে প্রস্থান করা।
ভিজিআর

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;
 }

এটি ব্যর্থ হওয়ার সম্ভাবনা রয়েছে, কারণ চেকআপডেটসটি এটি নয় volatileDocs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3 দেখুন ।
ভিজিআর

0

কখনও কখনও আমি আমার অনদাস্ট্রয় () / প্রসঙ্গেডাস্ট্রয়েড () এ 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();}
        }

    }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.