थ्रेड से रिटर्निंग वैल्यू


93

मैं एक के साथ एक विधि है HandlerThread। एक मान अंदर बदल जाता है Threadऔर मैं इसे test()विधि में वापस करना चाहता हूं । क्या इसे करने का कोई तरीका है?

public void test()
{   
    Thread uiThread = new HandlerThread("UIHandler"){
        public synchronized void run(){
            int value; 
            value = 2; //To be returned to test()
        }
    };
    uiThread.start();
}

यदि मुख्य थ्रेड विधि से लौटने से पहले हैंडलर थ्रेड के लिए इंतजार करना चाहिए, तो पहली जगह में हैंडलर थ्रेड का उपयोग क्यों करें?
बजे जेबी निज़ेट

1
@JBNizet I क्या वास्तव में थ्रेड वास्तव में करता है की जटिलता शामिल है। यह जीपीएस निर्देशांक हो रहा है तो हाँ मुझे एक धागा की आवश्यकता है।
नीता

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

@ जेबीएनज़ेट मुझे यकीन नहीं है कि आपका क्या मतलब है .. क्या आप इसे अलग तरीके से समझाएंगे?
नीता

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

जवाबों:


75

आप एक स्थानीय अंतिम चर सरणी का उपयोग कर सकते हैं। चर को गैर-आदिम प्रकार का होना चाहिए, ताकि आप एक सरणी का उपयोग कर सकें। आपको दो थ्रेड्स को सिंक्रनाइज़ करने की भी आवश्यकता है, उदाहरण के लिए एक काउंटडाउनलच का उपयोग करके :

public void test()
{   
    final CountDownLatch latch = new CountDownLatch(1);
    final int[] value = new int[1];
    Thread uiThread = new HandlerThread("UIHandler"){
        @Override
        public void run(){
            value[0] = 2;
            latch.countDown(); // Release await() in the test thread.
        }
    };
    uiThread.start();
    latch.await(); // Wait for countDown() in the UI thread. Or could uiThread.join();
    // value[0] holds 2 at this point.
}

आप एक Executorऔर Callableइस तरह का उपयोग कर सकते हैं :

public void test() throws InterruptedException, ExecutionException
{   
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Callable<Integer> callable = new Callable<Integer>() {
        @Override
        public Integer call() {
            return 2;
        }
    };
    Future<Integer> future = executor.submit(callable);
    // future.get() returns 2 or raises an exception if the thread dies, so safer
    executor.shutdown();
}

3
उम नहीं। यह कोड सही नहीं है। मूल्य तक पहुंच सही ढंग से सिंक्रनाइज़ नहीं है।
जी ब्लेक मीक

5
हम वास्तव में काउंटडाउनचैच की मेमोरी स्थिरता की गारंटी के कारण मूल्य तक पहुंच के लिए स्पष्ट सिंक्रनाइज़ेशन की आवश्यकता नहीं है। वैल्यू ऐरे क्रिएशन होता है-यूथ्रेड स्टार्ट (प्रोग्राम ऑर्डर नियम) से पहले जो कि 2 से मान के असाइनमेंट के साथ सिंक्रोनाइज़ करता है [0] ( थ्रेड स्टार्ट ) जो होता है-लैच से पहले -डाउनडाउन () (प्रोग्राम ऑर्डर नियम) जो होता है- लैच से पहले। .await () (CountDownLatch से गारंटी) जो होता है-मान [0] (प्रोग्राम ऑर्डर नियम) से पढ़ने से पहले।
एडम ज़ल्कमैन

लगता है जैसे आप कुंडी के बारे में सही हैं! ... किस स्थिति में, रन पद्धति का सिंक्रनाइज़ेशन बेकार है।
जी। ब्लेक मीक

अच्छी बात। मुझे ओपी कोड से कॉपी-पेस्ट करना होगा। सही किया।
एडम ज़ल्कमैन

यहां काउंटडाउन लैंथ का उदाहरण दिया गया है: developer.android.com/reference/java/util/concurrent/…
सीगल

88

आमतौर पर आप इसे कुछ इस तरह से करते होंगे

 public class Foo implements Runnable {
     private volatile int value;

     @Override
     public void run() {
        value = 2;
     }

     public int getValue() {
         return value;
     }
 }

फिर आप थ्रेड बना सकते हैं और मान प्राप्त कर सकते हैं (यह देखते हुए कि मान सेट किया गया है)

Foo foo = new Foo();
Thread thread = new Thread(foo);
thread.start();
thread.join();
int value = foo.getValue();

tl;drएक थ्रेड एक मान नहीं लौटा सकता (कम से कम बिना कॉलबैक तंत्र के)। आपको एक साधारण वर्ग की तरह एक सूत्र का संदर्भ देना चाहिए और मूल्य पूछना चाहिए।


2
क्या यह वास्तव में काम करता है? मुझे मिलता है The method getValue() is undefined for the type Thread
पीचन्ना

1
@pmichna, अच्छा खोलना। से बदल रहा t.getValue()है foo.getValue()
जोहान Sjöberg

3
हाँ! बहुत बढ़िया! "अस्थिर" ftw! स्वीकृत उत्तर के विपरीत, यह सही है!
जी। ब्लेक मीक

11
@ HamzahMalik थ्रेड फिनिश का उपयोग सुनिश्चित करने के लिए Thread t = new Thread(foo); t.start(); t.join(); foo.getValue();t.join()धागा जब तक ब्लॉक समाप्त हो गया है।
डेनियल

2
@ हरीकिरण हाँ सही है, प्रत्येक थ्रेड एक स्वतंत्र मूल्य देता है।
rootExplorr

28

आप जिस चीज की तलाश कर रहे हैं, वह संभवतः एक वस्तु के Callable<V>स्थान पर इंटरफेस है Runnable, और किसी Future<V>वस्तु के साथ मूल्य को पुनः प्राप्त करना , जो आपको मूल्य की गणना होने तक इंतजार करने की सुविधा देता है। आप इसे एक ExecutorServiceसे प्राप्त कर सकते हैं, जिसे आप प्राप्त कर सकते हैं Executors.newSingleThreadExecutor()

public void test() {
    int x;
    ExecutorService es = Executors.newSingleThreadExecutor();
    Future<Integer> result = es.submit(new Callable<Integer>() {
        public Integer call() throws Exception {
            // the other thread
            return 2;
        }
    });
    try {
        x = result.get();
    } catch (Exception e) {
        // failed
    }
    es.shutdown();
}

8

इस समाधान के बारे में कैसे?

यह थ्रेड क्लास का उपयोग नहीं करता है, लेकिन यह समवर्ती है, और एक तरह से यह वही करता है जो आप अनुरोध करते हैं

ExecutorService pool = Executors.newFixedThreadPool(2); // creates a pool of threads for the Future to draw from

Future<Integer> value = pool.submit(new Callable<Integer>() {
    @Override
    public Integer call() {return 2;}
});

अब आप सभी से यही कहते हैं कि value.get()जब भी आपको अपने लौटाए गए मूल्य को हथियाने की आवश्यकता हो, तो धागा शुरू किया जाता है, दूसरे को आप valueएक मूल्य देते हैं, इसलिए आपको कभी कहने की ज़रूरत नहीं हैthreadName.start() है।

क्या Futureहै, एक वादा है कार्यक्रम के लिए है, आप उस कार्यक्रम का वादा करते हैं जो आपको निकट भविष्य में कुछ समय के लिए इसकी आवश्यकता होगी

यदि आप .get()इसे पूरा करने से पहले कॉल करते हैं, तो जो थ्रेड इसे कॉल कर रहा है, वह बस तब तक इंतजार करेगा जब तक यह पूरा नहीं हो जाता


मैंने प्रदान की गई कोड को दो चीजों में विभाजित किया है एक है पूल दीक्षा, जो मैं एप्लिकेशन क्लास में करता हूं (मैं एंड्रॉइड के बारे में बात कर रहा हूं) और दूसरी बात मैं उन जगहों पर पूल का उपयोग करता हूं जहां मुझे इसकी आवश्यकता है ... इसके अलावा Executors.newFixiveThreadPool ( 2) मैंने Executors.newSingleThreadExecutor () का उपयोग किया है .. क्योंकि मुझे सर्वर कॉल के लिए एक समय में केवल एक ही कार्य चलाने की आवश्यकता है ... आपका उत्तर एकदम सही है @electirc कॉफी धन्यवाद
गौरव पंगम

@ बिजली आपको कैसे पता है कि कब मूल्य प्राप्त करना है? मेरा मानना ​​है कि मूल पोस्टर इस मूल्य को अपनी विधि में वापस करना चाहता था। .Get () कॉल का उपयोग करने से मान फिर से प्राप्त होगा, लेकिन केवल अगर ऑपरेशन पूरा हो गया है। वह एक अंधे .get () कॉल के साथ नहीं जानता
portfoliobuilder

4

यदि आप कॉलिंग विधि से मान चाहते हैं, तो इसे थ्रेड के समाप्त होने तक इंतजार करना चाहिए, जो थ्रेड्स को थोड़ा व्यर्थ कर देता है।

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

प्रश्न में कोड पर थोड़ा ध्यान दें: विस्तार Threadकरना आमतौर पर खराब शैली है। वास्तव में अनावश्यक रूप से कक्षाओं का विस्तार करना एक बुरा विचार है। मुझे लगता runहै कि आप विधि किसी कारण के लिए सिंक्रनाइज़ है। अब इस मामले में वस्तु के रूप में Threadआप Threadइसके लॉक का उपयोग करने के लिए हस्तक्षेप कर सकते हैं (संदर्भ कार्यान्वयन में join, IIRC के साथ कुछ करने के लिए )।


"फिर इसे थ्रेड के खत्म होने का इंतजार करना चाहिए, जो थ्रेड को थोड़ा व्यर्थ उपयोग करता है" महान बिंदु!
जैसे किजु

4
यह आमतौर पर व्यर्थ है, लेकिन एंड्रॉइड में आप मुख्य थ्रेड पर सर्वर के लिए नेटवर्क अनुरोध नहीं कर सकते (एप्लिकेशन को उत्तरदायी रखने के लिए) इसलिए आपको एक नेटवर्क थ्रेड का उपयोग करना होगा। ऐसे परिदृश्य हैं जिनमें एप्लिकेशन को फिर से शुरू करने से पहले आपको परिणाम की आवश्यकता होती है।
उदी ईदैन

2

जावा 8 के बाद से हमारे पास कंप्लीटटेबल सिवनी है। अपने मामले पर, आप निष्पादन के बाद परिणाम प्राप्त करने के लिए विधि supplyAsync का उपयोग कर सकते हैं ।

कृपया कुछ रेफरी ढूंढें। यहाँ https://www.baeldung.com/java-completablefuture#Processing

    CompletableFuture<Integer> completableFuture
      = CompletableFuture.supplyAsync(() -> yourMethod());

   completableFuture.get() //gives you the value

1

उपरोक्त उत्तरों में वर्णित Future का उपयोग करना काम करता है, लेकिन f.get () के रूप में थोड़ा कम महत्वपूर्ण है, परिणाम प्राप्त होने तक थ्रेड को ब्लॉक करता है, जो कि संगामिति का उल्लंघन करता है।

सबसे अच्छा उपाय है कि अमरूद की सुनने योग्य सामग्री का उपयोग करें। एक उदाहरण :

    ListenableFuture<Void> future = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1, new NamedThreadFactory).submit(new Callable<Void>()
    {
        @Override
        public Void call() throws Exception
        {
            someBackgroundTask();
        }
    });
    Futures.addCallback(future, new FutureCallback<Long>()
    {
        @Override
        public void onSuccess(Long result)
        {
            doSomething();
        }

        @Override
        public void onFailure(Throwable t)
        {

        }
    };

1

अपने कोड में छोटे संशोधनों के साथ, आप इसे अधिक सामान्य तरीके से प्राप्त कर सकते हैं।

 final Handler responseHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                //txtView.setText((String) msg.obj);
                Toast.makeText(MainActivity.this,
                        "Result from UIHandlerThread:"+(int)msg.obj,
                        Toast.LENGTH_LONG)
                        .show();
            }
        };

        HandlerThread handlerThread = new HandlerThread("UIHandlerThread"){
            public void run(){
                Integer a = 2;
                Message msg = new Message();
                msg.obj = a;
                responseHandler.sendMessage(msg);
                System.out.println(a);
            }
        };
        handlerThread.start();

उपाय :

  1. HandlerUI थ्रेड में बनाएँ , जिसे कहा जाता हैresponseHandler
  2. UI थ्रेड Handlerसे इसे प्रारंभ करें Looper
  3. में HandlerThread, इस पर संदेश पोस्ट करेंresponseHandler
  4. handleMessgaeToastसंदेश से प्राप्त मान के साथ दिखाता है । यह संदेश ऑब्जेक्ट सामान्य है और आप विभिन्न प्रकार के गुण भेज सकते हैं।

इस दृष्टिकोण के साथ, आप अलग-अलग समय पर UI थ्रेड के लिए कई मान भेज सकते हैं। आप Runnableइस पर कई ऑब्जेक्ट्स चला सकते हैं (पोस्ट कर सकते हैं) HandlerThreadऔर प्रत्येक ऑब्जेक्ट Runnableमें वैल्यू सेट कर सकते हैं Message, जिसे यूआई थ्रेड द्वारा प्राप्त किया जा सकता है।

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