मेरे पास एक विधि है जिसका नाम है StartDownload(), जिसमें तीन सूत्र हैं।
जब प्रत्येक थ्रेड निष्पादित हो जाता है तो मुझे एक सूचना कैसे मिलती है?
क्या यह जानने का कोई तरीका है कि क्या धागे का एक (या सभी) समाप्त हो गया है या अभी भी चल रहा है?
मेरे पास एक विधि है जिसका नाम है StartDownload(), जिसमें तीन सूत्र हैं।
जब प्रत्येक थ्रेड निष्पादित हो जाता है तो मुझे एक सूचना कैसे मिलती है?
क्या यह जानने का कोई तरीका है कि क्या धागे का एक (या सभी) समाप्त हो गया है या अभी भी चल रहा है?
जवाबों:
ऐसे कई तरीके हैं जिनसे आप यह कर सकते हैं:
आइडिया # 5 कैसे लागू करें? खैर, एक तरीका यह है कि पहले एक इंटरफ़ेस बनाया जाए:
public interface ThreadCompleteListener {
void notifyOfThreadComplete(final Thread thread);
}
फिर निम्न वर्ग बनाएं:
public abstract class NotifyingThread extends Thread {
private final Set<ThreadCompleteListener> listeners
= new CopyOnWriteArraySet<ThreadCompleteListener>();
public final void addListener(final ThreadCompleteListener listener) {
listeners.add(listener);
}
public final void removeListener(final ThreadCompleteListener listener) {
listeners.remove(listener);
}
private final void notifyListeners() {
for (ThreadCompleteListener listener : listeners) {
listener.notifyOfThreadComplete(this);
}
}
@Override
public final void run() {
try {
doRun();
} finally {
notifyListeners();
}
}
public abstract void doRun();
}
और फिर आपका प्रत्येक थ्रेड विस्तारित होगा NotifyingThreadऔर run()इसे लागू करने के बजाय इसे लागू करेगा doRun()। इस प्रकार जब वे पूर्ण हो जाते हैं, तो वे स्वचालित रूप से अधिसूचना की प्रतीक्षा कर रहे किसी को भी सूचित करेंगे।
अंत में, आपकी मुख्य कक्षा में - वह जो सभी थ्रेड्स शुरू करता है (या कम से कम ऑब्जेक्ट अधिसूचना के लिए इंतजार कर रहा है) - उस क्लास को संशोधित करें implement ThreadCompleteListenerऔर प्रत्येक थ्रेड बनाने के तुरंत बाद श्रोताओं की सूची में खुद को जोड़ें:
NotifyingThread thread1 = new OneOfYourThreads();
thread1.addListener(this); // add ourselves as a listener
thread1.start(); // Start the Thread
फिर, जैसा कि प्रत्येक थ्रेड से बाहर निकलता है, आपका notifyOfThreadCompleteतरीका थ्रेड उदाहरण के साथ लागू किया जाएगा जो अभी पूरा हुआ (या दुर्घटनाग्रस्त हो गया)।
ध्यान दें कि बेहतर होगा implements Runnableबजाय extends Threadके लिए NotifyingThreadके रूप में धागा विस्तार आमतौर पर नए कोड में हतोत्साहित किया जाता है। लेकिन मैं आपके सवाल का कोडिंग कर रहा हूं। यदि आप NotifyingThreadकार्यान्वित करने के लिए कक्षा बदलते हैं Runnableतो आपको अपना कुछ कोड बदलना होगा जो थ्रेड्स का प्रबंधन करता है, जो करने के लिए बहुत सरल है।
notifyविधि को कॉल न किया जाए run, लेकिन इसके बाद।
CyclicBarrier का उपयोग कर समाधान
public class Downloader {
private CyclicBarrier barrier;
private final static int NUMBER_OF_DOWNLOADING_THREADS;
private DownloadingThread extends Thread {
private final String url;
public DownloadingThread(String url) {
super();
this.url = url;
}
@Override
public void run() {
barrier.await(); // label1
download(url);
barrier.await(); // label2
}
}
public void startDownload() {
// plus one for the main thread of execution
barrier = new CyclicBarrier(NUMBER_OF_DOWNLOADING_THREADS + 1); // label0
for (int i = 0; i < NUMBER_OF_DOWNLOADING_THREADS; i++) {
new DownloadingThread("http://www.flickr.com/someUser/pic" + i + ".jpg").start();
}
barrier.await(); // label3
displayMessage("Please wait...");
barrier.await(); // label4
displayMessage("Finished");
}
}
लेबल 0 - चक्रीय अवरोध को निष्पादन थ्रेड्स की संख्या के बराबर पार्टियों की संख्या के साथ बनाया गया है, निष्पादन के मुख्य धागे के लिए एक (जिसमें startDownload () निष्पादित किया जा रहा है)
लेबल 1 - n-th DownloadingThread प्रतीक्षा कक्ष में प्रवेश करता है
लेबल 3 - NUMBER_OF_DOWNLOADING_THREADS ने प्रतीक्षालय में प्रवेश किया है। निष्पादन का मुख्य धागा उन्हें कम से कम एक ही समय में अपने डाउनलोडिंग कार्य करना शुरू करने के लिए जारी करता है
लेबल 4 - निष्पादन का मुख्य धागा प्रतीक्षालय में प्रवेश करता है। यह समझने के लिए कोड का 'पेचीदा' हिस्सा है। इससे कोई फर्क नहीं पड़ता कि कौन सा धागा दूसरी बार प्रतीक्षा कक्ष में प्रवेश करेगा। यह महत्वपूर्ण है कि जो भी थ्रेड कमरे में प्रवेश करता है वह अंतिम सुनिश्चित करता है कि अन्य सभी डाउनलोडिंग थ्रेड ने अपनी डाउनलोडिंग नौकरियां समाप्त कर ली हैं।
लेबल 2 - n-th DownloadingThread ने अपना डाउनलोडिंग कार्य पूरा कर लिया है और प्रतीक्षा कक्ष में प्रवेश करता है। यदि यह अंतिम एक है या पहले से ही NUMBER_OF_DOWNLOADING_THREADS ने इसे दर्ज किया है, जिसमें निष्पादन का मुख्य धागा शामिल है, मुख्य धागा केवल तब ही इसका निष्पादन जारी रखेगा, जब अन्य सभी धागे डाउनलोड करना समाप्त कर देंगे।
आपको वास्तव में एक समाधान का उपयोग करना चाहिए java.util.concurrent। विषय पर जोश बलोच और / या ब्रायन गोएट्ज को खोजें और पढ़ें।
यदि आप उपयोग नहीं कर java.util.concurrent.*रहे हैं और सीधे थ्रेड्स का उपयोग करने की जिम्मेदारी ले रहे हैं, तो आपको संभवतः join()यह जानने के लिए उपयोग करना चाहिए कि थ्रेड कब किया जाता है। यहाँ एक सुपर सरल कॉलबैक तंत्र है। Runnableकॉलबैक करने के लिए पहले इंटरफ़ेस का विस्तार करें :
public interface CallbackRunnable extends Runnable {
public void callback();
}
फिर एक एग्जिक्युटिव बनाइए जो आपके रन करने योग्य को निष्पादित करेगा और जब यह हो जाएगा तब आपको कॉल करेगा।
public class CallbackExecutor implements Executor {
@Override
public void execute(final Runnable r) {
final Thread runner = new Thread(r);
runner.start();
if ( r instanceof CallbackRunnable ) {
// create a thread to perform the callback
Thread callerbacker = new Thread(new Runnable() {
@Override
public void run() {
try {
// block until the running thread is done
runner.join();
((CallbackRunnable)r).callback();
}
catch ( InterruptedException e ) {
// someone doesn't want us running. ok, maybe we give up.
}
}
});
callerbacker.start();
}
}
}
आपके CallbackRunnableइंटरफ़ेस में जोड़ने के लिए अन्य प्रकार की स्पष्ट चीज़ किसी भी अपवाद को संभालने का एक साधन है, इसलिए हो सकता है कि public void uncaughtException(Throwable e);वहाँ एक लाइन डालें और आपके निष्पादक में, उस इंटरफ़ेस विधि को भेजने के लिए एक Thread.UncaughtExceptionHandler स्थापित करें।
लेकिन वह सब करना वास्तव में जैसे बदबू करने लगता है java.util.concurrent.Callable। java.util.concurrentयदि आपकी परियोजना इसकी अनुमति देती है तो आपको वास्तव में इसका उपयोग करना चाहिए ।
runner.join()और फिर उसके बाद जो भी कोड आप चाहते हैं, जब से आप जानते हैं कि धागा समाप्त हो गया है। क्या यह सिर्फ इतना है कि आपको उस कोड को रन करने योग्य की संपत्ति के रूप में परिभाषित करना है, इसलिए आपके पास अलग-अलग रनवे के लिए अलग-अलग चीजें हो सकती हैं?
runner.join()इंतजार करने का सबसे सीधा तरीका है। मैं मान रहा था कि ओपी अपने मुख्य कॉलिंग थ्रेड को ब्लॉक नहीं करना चाहता था क्योंकि वे प्रत्येक डाउनलोड के लिए "अधिसूचित" होने को कहते थे, जो किसी भी क्रम में पूरा हो सकता था। इसने अतुल्यकालिक रूप से अधिसूचित होने का एक तरीका पेश किया।
क्या आप उन्हें खत्म करने के लिए इंतजार करना चाहते हैं? यदि हां, तो जॉइन विधि का उपयोग करें।
यदि आप इसे बस चेक करना चाहते हैं, तो इसमें अलग-अलग संपत्ति भी है।
Threadको बताते हैं start, जो थ्रेड करता है, लेकिन कॉल तुरंत लौटता है। isAliveएक साधारण ध्वज परीक्षण होना चाहिए, लेकिन जब मैंने इसे देखा तो यह विधि थी native।
आप getState () के साथ थ्रेड इंस्टेंस पर पूछताछ कर सकते हैं, जो थ्रेड की एक आवृत्ति लौटाता है। निम्नलिखित मूल्यों में से एक के साथ एन्यूमरेशन करें:
* NEW
A thread that has not yet started is in this state.
* RUNNABLE
A thread executing in the Java virtual machine is in this state.
* BLOCKED
A thread that is blocked waiting for a monitor lock is in this state.
* WAITING
A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
* TIMED_WAITING
A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
* TERMINATED
A thread that has exited is in this state.
हालाँकि मुझे लगता है कि यह एक बेहतर डिज़ाइन होगा जिसमें मास्टर धागा होगा जो 3 बच्चों के समाप्त होने की प्रतीक्षा करता है, मास्टर तब निष्पादन जारी रखेगा जब अन्य 3 समाप्त हो जाएंगे।
आप ExecutorService थ्रेड पूल Executorsबनाने के लिए ऑब्जेक्ट का उपयोग भी कर सकते हैं । फिर अपने प्रत्येक धागे को चलाने के लिए और फ्यूचर्स को पुनः प्राप्त करने के लिए विधि का उपयोग करें । यह तब तक अवरुद्ध होगा जब तक सभी निष्पादन समाप्त नहीं हो जाते। आपका दूसरा विकल्प पूल का उपयोग करके प्रत्येक को निष्पादित करना होगा और तब तक ब्लॉक को कॉल करना होगा जब तक कि पूल निष्पादित न हो जाए। जब आप कार्य जोड़ रहे हों तो बस कॉल करना सुनिश्चित करें ()।invokeAllawaitTerminationshutdown
मल्टी थ्रेडिंग मोर्चे पर पिछले 6 सालों में कई चीजें बदली गई हैं।
join()API का उपयोग करने और लॉक करने के बजाय , आप उपयोग कर सकते हैं
1. एक्ज़ीक्यूसर सर्विस invokeAll() एपीआई
दिए गए कार्यों को निष्पादित करता है, सभी पूर्ण होने पर अपनी स्थिति और परिणामों को पकड़े हुए फ्यूचर्स की सूची लौटाते हैं।
2. काउंटडाउनचैट
एक सिंक्रनाइज़ेशन सहायता जो एक या अधिक थ्रेड्स को प्रतीक्षा करने की अनुमति देती है जब तक कि अन्य थ्रेड्स में किए गए संचालन का एक सेट पूरा नहीं हो जाता।
A
CountDownLatchको एक दी गई गणना के साथ आरंभीकृत किया गया है। प्रतीक्षा पद्धति तब तक अवरुद्ध होती है जब तक किcountDown()विधि की चाल के कारण वर्तमान गिनती शून्य तक नहीं पहुंचती है , जिसके बाद सभी प्रतीक्षा धागे जारी होते हैं और प्रतीक्षा के किसी भी बाद के चालान तुरंत वापस आ जाते हैं। यह एक-शॉट वाली घटना है - गिनती को रीसेट नहीं किया जा सकता है। यदि आपको एक संस्करण की आवश्यकता है जो गिनती को रीसेट करता है, तो एक साइक्लिक बैरियर का उपयोग करने पर विचार करें।
3. ForkJoinPool या newWorkStealingPool()में निष्पादकों दूसरा रास्ता है
4. सभी Futureकार्यों के माध्यम से सबमिट करें ExecutorServiceऔर ऑब्जेक्ट get()पर अवरुद्ध कॉल के साथ स्थिति की जांच करेंFuture
संबंधित एसई प्रश्नों पर एक नजर:
मैं सुझाव दूंगा कि थ्रेड क्लास के लिए जावदोक देखना ।
धागा हेरफेर के लिए आपके पास कई तंत्र हैं।
आपका मुख्य धागा join()तीन धागे को क्रमिक रूप से ले सकता है, और तब तक आगे नहीं बढ़ेगा जब तक कि तीनों नहीं हो जाते।
अंतराल पर थ्रेडेड थ्रेड्स की थ्रेड स्थिति को पोल करें।
सभी थ्रेडेड थ्रेड्स को एक अलग में रखें ThreadGroupऔर उस activeCount()पर पोल करें ThreadGroupऔर 0 पर जाने के लिए प्रतीक्षा करें।
अंतर-थ्रेड संचार के लिए एक कस्टम कॉलबैक या श्रोता प्रकार के इंटरफ़ेस को सेट करें।
मुझे यकीन है कि अन्य बहुत सारे तरीके हैं जो मुझे अभी भी याद हैं।
यहाँ एक समाधान है जो सरल, संक्षिप्त, समझने में आसान है, और मेरे लिए पूरी तरह से काम करता है। जब एक और धागा समाप्त होता है तो मुझे स्क्रीन पर आकर्षित होने की आवश्यकता होती है; लेकिन नहीं कर सकता क्योंकि मुख्य धागे में स्क्रीन का नियंत्रण है। इसलिए:
(1) मैंने वैश्विक वैरिएबल बनाया है: boolean end1 = false;थ्रेड अंत होने पर इसे सही पर सेट करता है। यह "पोस्टडेलिड" लूप द्वारा मुख्य स्थान पर उठाया जाता है, जहां इसका जवाब दिया जाता है।
(2) मेरे धागे में शामिल हैं:
void myThread() {
end1 = false;
new CountDownTimer(((60000, 1000) { // milliseconds for onFinish, onTick
public void onFinish()
{
// do stuff here once at end of time.
end1 = true; // signal that the thread has ended.
}
public void onTick(long millisUntilFinished)
{
// do stuff here repeatedly.
}
}.start();
}
(3) सौभाग्य से, "पोस्टडेलिड" मुख्य धागे में चलता है, इसलिए यहीं से हर एक बार दूसरे धागे की जाँच करें। जब दूसरा धागा समाप्त होता है, तो यह वह शुरू कर सकता है जो हम आगे करना चाहते हैं।
Handler h1 = new Handler();
private void checkThread() {
h1.postDelayed(new Runnable() {
public void run() {
if (end1)
// resond to the second thread ending here.
else
h1.postDelayed(this, 1000);
}
}, 1000);
}
(४) अंत में, कॉल करके अपने कोड में कहीं चलने वाली पूरी चीज़ शुरू करें:
void startThread()
{
myThread();
checkThread();
}
मुझे लगता है कि ThreadPoolExecutorकक्षा का उपयोग करने का सबसे आसान तरीका है ।
हुक के तरीके
यह वर्ग संरक्षित अधिलेखनीय
beforeExecute(java.lang.Thread, java.lang.Runnable)औरafterExecute(java.lang.Runnable, java.lang.Throwable)तरीके प्रदान करता है जिन्हें प्रत्येक कार्य के निष्पादन से पहले और बाद में कहा जाता है। इनका उपयोग निष्पादन पर्यावरण में हेरफेर करने के लिए किया जा सकता है; उदाहरण के लिए, थ्रेडलोकल्स को फिर से संगठित करना, आंकड़े इकट्ठा करना, या लॉग प्रविष्टियों को जोड़ना। इसके अतिरिक्त,terminated()किसी भी विशेष प्रसंस्करण को निष्पादित करने के लिए विधि को ओवरराइड किया जा सकता है जिसे एक्जिक्यूटर के पूरी तरह से समाप्त हो जाने के बाद करने की आवश्यकता होती है।
जो ठीक वैसा ही है जैसा हमें चाहिए। हम afterExecute()प्रत्येक थ्रेड के बाद कॉलबैक प्राप्त करने के लिए ओवरराइड करेंगे और यह terminated()जानने के लिए ओवरराइड करेंगे कि सभी थ्रेड्स कब किए जाते हैं।
तो यहाँ है कि आपको क्या करना चाहिए
एक निष्पादक बनाएं:
private ThreadPoolExecutor executor;
private int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private void initExecutor() {
executor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, //core pool size
NUMBER_OF_CORES * 2, //max pool size
60L, //keep aive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
//Yet another thread is finished:
informUiAboutProgress(executor.getCompletedTaskCount(), listOfUrisToProcess.size());
}
}
};
@Override
protected void terminated() {
super.terminated();
informUiThatWeAreDone();
}
}और अपने सूत्र शुरू करें:
private void startTheWork(){
for (Uri uri : listOfUrisToProcess) {
executor.execute(new Runnable() {
@Override
public void run() {
doSomeHeavyWork(uri);
}
});
}
executor.shutdown(); //call it when you won't add jobs anymore
}informUiThatWeAreDone();जब सभी थ्रेड्स किए जाते हैं, तो अंदर की विधि जो भी करना है, उदाहरण के लिए, यूआई को अपडेट करें।
नोट:synchronized यदि आप synchronizedकिसी अन्य synchronizedविधि से कॉल करने का निर्णय लेते हैं, तो समानांतर और BE VERY CAUTIOUS में अपना काम करने के बाद से विधियों का उपयोग करना न भूलें ! यह अक्सर गतिरोध की ओर जाता है
उम्मीद है की यह मदद करेगा!
आप SwingWorker का उपयोग भी कर सकते हैं, जिसमें अंतर्निहित संपत्ति परिवर्तन समर्थन है। देखें addPropertyChangeListener () या प्राप्त () एक राज्य परिवर्तन श्रोता उदाहरण के लिए विधि।
थ्रेड वर्ग के लिए जावा दस्तावेज़ देखें। आप थ्रेड की स्थिति की जांच कर सकते हैं। यदि आप तीन थ्रेड्स को सदस्य चर में रखते हैं, तो सभी तीन थ्रेड एक दूसरे के राज्यों को पढ़ सकते हैं।
आपको थोड़ा सावधान रहना होगा, हालांकि, क्योंकि आप थ्रेड्स के बीच दौड़ की स्थिति पैदा कर सकते हैं। बस दूसरे थ्रेड्स की स्थिति के आधार पर जटिल तर्क से बचने की कोशिश करें। निश्चित रूप से एक ही चर के लिए कई सूत्र लिखने से बचें।