থ্রেড লাইফসাইকেল: অবস্থা ও স্থানান্তর
জাভার প্রতিটি Thread অবজেক্ট, তৈরি হওয়ার মুহূর্ত থেকে শেষ হওয়ার মুহূর্ত পর্যন্ত, সর্বদা Thread.State এনাম দ্বারা সংজ্ঞায়িত ছয়টি অবস্থার মধ্যে ঠিক একটিতে থাকে। এই অবস্থাগুলো জানা, এবং আরও গুরুত্বপূর্ণভাবে একটি থ্রেড কেন এদের মধ্যে স্থানান্তরিত হয় তা বোঝা, আপনাকে থ্রেড ডাম্প পড়ে হতবাক হয়ে তাকিয়ে না থেকে সমস্যা নির্ণয় করার ক্ষমতা দেয়।
ছয়টি অবস্থা
NEWমানেThreadঅবজেক্ট তৈরি হয়েছে কিন্তু এখনওstart()কল করা হয়নি; কোনো OS থ্রেড এখনো বিদ্যমান নয়।RUNNABLEমানে থ্রেডটি নির্বাহ করছে, অথবা এটি নির্বাহের জন্য প্রস্তুত এবং OS শিডিউলার তাকে CPU সময় দেওয়ার জন্য অপেক্ষা করছে — জাভা "প্রকৃতপক্ষে চলছে" এবং "চলার যোগ্য ও কোরের জন্য অপেক্ষমান" এর মধ্যে পার্থক্য করে না।BLOCKEDমানে থ্রেডটি একটি ইনট্রিনসিক লক অর্জনের জন্য অপেক্ষা করছে, যেমনsynchronizedব্লক, যা বর্তমানে অন্য কোনো থ্রেড ধরে রেখেছে।WAITINGমানে থ্রেডটি অনির্দিষ্টকাল ধরে অন্য কোনো থ্রেডের কোনো কাজ করার জন্য অপেক্ষা করছে, সাধারণতObject.wait(),Thread.join(), অথবাLockSupport.park()এর মাধ্যমে।TIMED_WAITINGহলোWAITINGএর মতোই কিন্তু একটি সময়সীমা সহ, যেমনThread.sleep(ms),Object.wait(ms), অথবাjoin(ms)কলের মাধ্যমে।TERMINATEDমানে থ্রেডেরrun()মেথড রিটার্ন করেছে অথবা ব্যতিক্রম ছুঁড়েছে, এবং এটি আর কখনো চলবে না।
স্থানান্তরগুলো পর্যবেক্ষণ
অবস্থাগুলো তখনই বেশি স্পষ্ট হয় যখন আপনি একটি থ্রেডকে এদের মধ্য দিয়ে চলতে দেখেন:
public class LifecycleDemo {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread worker = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(200);
lock.wait(500);
} catch (InterruptedException ignored) {
}
}
}, "worker");
System.out.println("শুরুর আগে: " + worker.getState()); // NEW
worker.start();
Thread.sleep(50);
System.out.println("শুরুর ঠিক পরে: " + worker.getState()); // RUNNABLE অথবা TIMED_WAITING
worker.join();
System.out.println("যোগদানের পরে: " + worker.getState()); // TERMINATED
}
}
এই কোড একটি নতুন থ্রেড তৈরি করে যা প্রথমে একটি লক ধরে, তারপর কিছু সময় ঘুমায় এবং সবশেষে লকটি ছেড়ে অপেক্ষায় চলে যায়। প্রথম আউটপুট দেখায় থ্রেডটি NEW অবস্থায় আছে, কার্যকর করার পর RUNNABLE বা TIMED_WAITING অবস্থায় প্রবেশ করে, এবং join() সম্পন্ন হলে TERMINATED অবস্থায় পৌঁছায়।
wait() থ্রেড অপেক্ষারত থাকাকালে লকটি ছেড়ে দেয়, এবং প্রথমেই লকটি ধরে রাখা বাধ্যতামূলক — সমন্বয় বিষয়ক পরবর্তী পর্বগুলোতে এ সংক্রান্ত আরও নিয়ম আলোচিত হবে।
BLOCKED বনাম WAITING: একটি সাধারণ বিভ্রান্তি
উভয় অবস্থাতেই মনে হয় "থ্রেডটি কিছু করছে না," কিন্তু কারণ ভিন্ন, এবং সমাধানও ভিন্ন:
public class BlockedDemo {
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
Thread holder = new Thread(BlockedDemo::hogLock, "holder");
Thread waiter = new Thread(BlockedDemo::hogLock, "waiter");
holder.start();
Thread.sleep(100);
waiter.start();
Thread.sleep(100);
System.out.println(waiter.getState()); // BLOCKED
holder.join();
waiter.join();
}
private static void hogLock() {
synchronized (LOCK) {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
}
}
এখানে দুটি থ্রেড একই লক পাওয়ার চেষ্টা করে। holder থ্রেডটি আগে শুরু হয় এবং এক সেকেন্ডের জন্য লকটি ধরে রাখে। waiter থ্রেডটি কিছুক্ষণ পরে শুরু হয় এবং লকটি পেতে না পেরে BLOCKED অবস্থায় চলে যায়, কারণ এটি এমন একটি লক চায় যা অন্য কেউ ধরে রেখেছে।
যদি থ্রেডটি LOCK.wait(), join(), অথবা park() কল করত, তাহলে এটি WAITING অথবা TIMED_WAITING হিসেবে প্রদর্শিত হতো — এগুলো স্বেচ্ছামূলক, সহযোগিতামূলক অপেক্ষার অবস্থা, যা প্রোগ্রামের যুক্তি অনুযায়ী নিয়ন্ত্রিত হয়।