আমার নামের একটি পদ্ধতিযুক্ত একটি অবজেক্ট রয়েছে 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
পদ্ধতিটি ইনসিন্ড না করা হলেও এর পরে করা উচিত।
সাইক্লিকবারিয়ার ব্যবহার করে সমাধান
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");
}
}
লেবেল ০ - এক্সিকিউটিভের মূল থ্রেডের জন্য নির্বাহী থ্রেডের সংখ্যার সমান পার্টির সংখ্যা সমেত চক্রীয় বাধা তৈরি করা হয় (যেখানে স্টার্টডাউনলোড () কার্যকর করা হচ্ছে)
লেবেল 1 - এন-তম ডাউনলোডিংথ্রেড ওয়েটিং রুমে প্রবেশ করে
লেবেল 3 - NUMBER_OF_DOWNLOADING_THREADS ওয়েটিং রুমে প্রবেশ করেছে। মৃত্যুদন্ড কার্যকর করার মূল থ্রেড একই সাথে কমবেশি তাদের ডাউনলোডের কাজগুলি শুরু করতে মুক্তি দেয়
লেবেল 4 - মৃত্যুদন্ডের মূল থ্রেড ওয়েটিং রুমে প্রবেশ করে। এটি কোডের 'কৌশলযুক্ত' অংশটি বোঝার জন্য। কোন থ্রেড দ্বিতীয়বারের জন্য ওয়েটিং রুমে প্রবেশ করবে তা বিবেচ্য নয়। এটি গুরুত্বপূর্ণ যে সর্বশেষ থ্রেডটি ঘরে প্রবেশ করে তা নিশ্চিত করে যে অন্যান্য ডাউনলোডের থ্রেডগুলি তাদের ডাউনলোড করার কাজ শেষ করেছে।
লেবেল 2 - এন-তম ডাউনলোডিংথ্রেড এটি ডাউনলোড করার কাজ শেষ করে ওয়েটিং রুমে প্রবেশ করেছে। যদি এটি শেষ হয় তবে ইতিমধ্যে 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);
সেখানে এবং আপনার নির্বাহকের মধ্যে একটি লাইন রাখুন, আপনাকে একটি ইন্টারফেস পদ্ধতিতে প্রেরণের জন্য একটি থ্রেড ইনস্টল করুন U
তবে যা আসলেই গন্ধ পেতে শুরু করে সেগুলি করা java.util.concurrent.Callable
। java.util.concurrent
আপনার প্রকল্পটির অনুমতি দিলে আপনার ব্যবহারের দিকে সত্যই নজর দেওয়া উচিত ।
runner.join()
এবং তারপরে আপনি যা যা কোড চান তার পরে আপনি জানেন যে থ্রেডটি শেষ হয়েছে has এটি কি কেবল রান কোডের সম্পত্তি হিসাবে সেই কোডটি সংজ্ঞায়িত করতে পেরেছেন, যাতে আপনার বিভিন্ন রানকেটে আলাদা আলাদা জিনিস থাকতে পারে?
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 টি শেষ হয়ে গেলে মাস্টার মৃত্যুদন্ড কার্যকর করবেন।
এক্সিকিউটর সার্ভিস থ্রেড পুল Executors
তৈরি করতে আপনি এই বস্তুটিও ব্যবহার করতে পারেন । তারপরে invokeAll
আপনার প্রতিটি থ্রেড চালাতে এবং ফিউচারগুলি পুনরুদ্ধার করতে পদ্ধতিটি ব্যবহার করুন । এটি কার্যকর হবে যতক্ষণ না সকলের কাজ শেষ হয়। আপনার অন্য বিকল্পটি awaitTermination
হ'ল পুলটি ব্যবহার করে প্রত্যেককে মৃত্যুদণ্ড কার্যকর করা এবং তারপরে পুলটি কার্যকর হওয়া শেষ না হওয়া অবধি ব্লকে কল করা। shutdown
আপনার কাজগুলি যুক্ত করার পরে কেবল () কল করতে ভুলবেন না ।
মাল্টি-থ্রেডিং ফ্রন্টে গত 6 বছরে অনেক কিছুই পরিবর্তন করা হয়েছে।
join()
এপিআই ব্যবহার এবং লক করার পরিবর্তে আপনি ব্যবহার করতে পারেন
1. এক্সিকিউটর সার্ভিস invokeAll()
এপিআই
প্রদত্ত টাস্কগুলি কার্যকর করে ফিউচারগুলির একটি তালিকা ফিরিয়ে তাদের স্ট্যাটাস এবং ফলাফলগুলি সম্পূর্ণ হয়ে গেলে।
একটি সিঙ্ক্রোনাইজেশন সহায়তা যা অন্য থ্রেডে পরিচালিত ক্রিয়াকলাপের সেট সম্পূর্ণ না হওয়া পর্যন্ত এক বা একাধিক থ্রেড অপেক্ষা করতে দেয়।
ক
CountDownLatch
একটি প্রদত্ত গণনা দিয়ে আরম্ভ করা হয়। পদ্ধতির অনুরোধের কারণে বর্তমান গণনা শূন্যের না হওয়া পর্যন্ত অপেক্ষা করা পদ্ধতিগুলি অবরুদ্ধ হয়ে যায়countDown()
, যার পরে সমস্ত অপেক্ষার থ্রেড প্রকাশিত হয় এবং তত্ক্ষণাত প্রত্যাবর্তনের প্রত্যাবর্তনের অনুরোধ জানানো হয়। এটি একটি শট ঘটনা - গণনাটি পুনরায় সেট করা যায় না। আপনার যদি এমন কোনও সংস্করণ প্রয়োজন হয় যা গণনাটিকে পুনরায় সেট করে, সাইক্লিকবারিয়ার ব্যবহার বিবেচনা করুন।
3. ForkJoinPool বা newWorkStealingPool()
মধ্যে নির্বাহক অন্যান্য উপায়
4. Future
জমা দেওয়া থেকে সমস্ত কার্যের মাধ্যমে সনাক্ত করুন ExecutorService
এবং অবরুদ্ধ কল সাথে স্ট্যাটাসটি পরীক্ষা get()
করুনFuture
বস্তু
সম্পর্কিত এসই সংক্রান্ত প্রশ্নগুলি একবার দেখুন:
এমন থ্রেডের জন্য কীভাবে অপেক্ষা করতে হবে যা এটি নিজের থ্রেডকে ছড়িয়ে দিয়েছে?
আমি থ্রেড ক্লাসের জাভাদোকের দিকে তাকানোর পরামর্শ দেব ।
থ্রেড ম্যানিপুলেশনের জন্য আপনার একাধিক প্রক্রিয়া রয়েছে।
আপনার মূল থ্রেডটি join()
তিনটি থ্রেড ক্রমিকভাবে করতে পারে এবং ততক্ষণে তিনটি সম্পন্ন না হওয়া পর্যন্ত অগ্রসর হবে না।
বিরতিতে প্রসারিত থ্রেডগুলির থ্রেডের অবস্থাটি পোল করুন।
একটি পৃথক মধ্যে উত্পন্ন হওয়া থ্রেডের সব রাখুন ThreadGroup
ও ভোটের activeCount()
উপর ThreadGroup
এবং এটি 0 পেতে জন্য অপেক্ষা করুন।
আন্তঃ থ্রেড যোগাযোগের জন্য একটি কাস্টম কলব্যাক বা শ্রোতার ধরণের ইন্টারফেস সেটআপ করুন।
আমি নিশ্চিত যে আমি এখনও নিখোঁজ প্রচুর অন্যান্য উপায় রয়েছে।
এখানে একটি সমাধান যা সহজ, সংক্ষিপ্ত, সহজে বোঝা যায় এবং আমার পক্ষে পুরোপুরি কাজ করে। যখন অন্য থ্রেডটি শেষ হবে তখন আমার স্ক্রিনে আঁকতে হবে; তবে পারেনি কারণ মূল থ্রেডটির স্ক্রিনের নিয়ন্ত্রণ রয়েছে। তাই:
(1) আমি বিশ্বব্যাপী পরিবর্তনশীল তৈরি করেছি: boolean end1 = false;
থ্রেডটি শেষ হওয়ার সাথে সাথে এটি সত্যে সেট করে। এটি মূল পোস্টে "পোস্টডিলিয়েড" লুপটি নিয়ে যায়, যেখানে এটির প্রতিক্রিয়া জানানো হয়।
(২) আমার থ্রেডে রয়েছে:
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
পদ্ধতি ! এটি প্রায়শই অচলাবস্থার দিকে পরিচালিত করে
আশাকরি এটা সাহায্য করবে!
আপনি সুইং ওয়ার্কারও ব্যবহার করতে পারেন, এতে বিল্ট-ইন সম্পত্তি পরিবর্তন সমর্থন রয়েছে। দেখুন addPropertyChangeListener () বা পেতে () একটি রাষ্ট্র পরিবর্তন শ্রোতা উদাহরণস্বরূপ পদ্ধতি।
থ্রেড ক্লাসের জন্য জাভা ডকুমেন্টেশন দেখুন। আপনি থ্রেডের অবস্থা পরীক্ষা করতে পারেন। আপনি যদি তিনটি থ্রেডকে সদস্যের ভেরিয়েবলগুলিতে রাখেন তবে তিনটি থ্রেডই একে অপরের রাজ্যগুলি পড়তে পারে।
আপনাকে কিছুটা সতর্ক হতে হবে, যদিও আপনি থ্রেডগুলির মধ্যে বর্ণের পরিস্থিতি তৈরি করতে পারেন। অন্যান্য থ্রেডের অবস্থার ভিত্তিতে জটিল যুক্তি এড়াতে চেষ্টা করুন। অবশ্যই একই ভেরিয়েবলগুলিতে একাধিক থ্রেড লেখা এড়ানো উচিত।