Thread bukan makhluk biner yang hanya “hidup” atau “sudah selesai” — JVM melacak perjalanan setiap thread melalui enam state yang terdefinisi rapi di enum Thread.State. Mengenal state-state tersebut bukan pelengkap teori: saat aplikasi tiba-tiba lambat atau berhenti merespons, dump thread menampilkan state setiap thread, dan kemampuan membacanya menentukan cepat atau lambatnya kita menemukan akar masalah. Chapter ini memetakan seluruh state beserta pemicu perpindahannya, lalu membuktikan lima state secara nyata lewat method getState().
Enam State Thread #
Enum Thread.State mendefinisikan enam nilai berikut:
NEW— objekThreadsudah dibuat, tetapistart()belum dipanggil. Objek hanyalah wadah biasa; thread sistem operasi belum ada.RUNNABLE— thread sedang dieksekusi atau siap dieksekusi. Java menyatukan dua kondisi ini; detail apakah thread benar-benar mengisi core CPU atau hanya menunggu giliran adalah urusan penjadwal sistem operasi.BLOCKED— thread menunggu intrinsic lock agar bisa masuk ke wilayahsynchronizedyang sedang dipegang thread lain.WAITING— thread menunggu tanpa batas waktu, misalnya karena memanggilwait(),join()tanpa argumen, atauLockSupport.park(). Thread hanya bangkit bila ada aksi eksplisit dari thread lain:notify(), selesainya thread yang di-join(), atauunpark().TIMED_WAITING— sama sepertiWAITING, tetapi dengan batas waktu:sleep(millis),join(millis), atauwait(millis). Bila waktu habis, thread kembaliRUNNABLEmeski tanpa sinyal dari siapa pun.TERMINATED— methodrun()sudah selesai (normal atau karena exception). Thread tidak bisa hidup kembali; objekThreadboleh dibaca state-nya, tetapistart()kedua melemparIllegalThreadStateException.
Peta Transisi State #
Seluruh perpindahan state dapat digambar menjadi satu diagram. Thread hanya bisa masuk ke jalur eksekusi lewat start(), dan hanya keluar dari RUNNABLE menuju TERMINATED ketika run() selesai:
Tiga state menunggu (BLOCKED, WAITING, TIMED_WAITING) terlihat serupa tetapi penyebabnya berbeda dan obatnya juga berbeda. Thread BLOCKED sembuh sendiri begitu pemegang lock melepaskannya; thread WAITING butuh thread lain yang bertindak; thread TIMED_WAITING diberi kesempatan bangkit sendiri ketika jatah waktunya habis.
Mengintip State dengan getState() #
Method getState() mengembalikan Thread.State saat pemanggilan berlangsung. Karena state bisa berubah kapan saja, pembacaan yang bermakna membutuhkan momen yang terkendali — itulah strategi program berikut: empat thread disusun dengan jeda waktu yang cukup lebar sehingga pada saat kita mengintip, posisi setiap thread pasti sudah stabil di state yang dituju:
public class SiklusState {
private static final Object kunci = new Object();
public static void main(String[] args) throws InterruptedException {
String[] catatan = new String[5];
// sleeper: tidur 300 ms, selama itu state-nya TIMED_WAITING.
Thread sleeper = new Thread(() -> {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "sleeper");
// worker: memegang lock "kunci" sambil tidur 400 ms.
Thread worker = new Thread(() -> {
synchronized (kunci) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "worker");
// waiter: menunggu thread sleeper selesai lewat join tanpa batas waktu.
Thread waiter = new Thread(() -> {
try {
sleeper.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "waiter");
// contender: akan mengantre pada lock "kunci" yang sedang dipegang worker.
Thread contender = new Thread(() -> {
synchronized (kunci) {
// masuk ke sini hanya setelah worker melepas kunci
}
}, "contender");
catatan[0] = "sebelum start : " + worker.getState(); // NEW
sleeper.start();
worker.start();
Thread.sleep(50); // beri waktu worker mengambil kunci dan sleeper mulai tidur
waiter.start();
contender.start();
Thread.sleep(100); // beri waktu contender mengantre dan waiter menunggu
catatan[1] = "sedang tidur : " + sleeper.getState(); // TIMED_WAITING
catatan[2] = "menunggu join : " + waiter.getState(); // WAITING
catatan[3] = "mengantre kunci : " + contender.getState(); // BLOCKED
sleeper.join();
waiter.join();
contender.join();
worker.join();
catatan[4] = "setelah selesai : " + worker.getState(); // TERMINATED
for (String baris : catatan) {
System.out.println(baris);
}
}
}Output yang Dihasilkan:
sebelum start : NEW
sedang tidur : TIMED_WAITING
menunggu join : WAITING
mengantre kunci : BLOCKED
setelah selesai : TERMINATEDMari kita telaah baris-baris kode di atas:
- Baris 13-17 (
Thread sleeper = ...): threadsleepertidur 300 milidetik, sehingga berada diTIMED_WAITINGselama periode tersebut. - Baris 19-26 (
Thread worker = ...): threadworkermasuk bloksynchronized(kunci)lalu tidur 400 milidetik — lock tetap dipegang selama tidur di dalam blok. - Baris 39 (
catatan[0] = ...):workerbelum di-start(), jadi state-nya pastiNEW— momen ini satu-satunya kesempatan legal membacaNEW. - Baris 42-43 (
Thread.sleep(50)): jeda 50 milidetik memberiworkerwaktu mengambil lock sebelumcontenderdibuat; tanpa jeda ini,contenderbisa lebih dulu memegang lock dan mengubah susunan drama. - Baris 49-51 (
catatan[1..3] = ...): pada momen t=150 ms,sleepermasih tidur (sampai 300 ms),waitermasih menunggusleeper, dancontendermasih mengantre lock (sampai ±400 ms). Ketiga pembacaan dilakukan threadmaindan disimpan ke array, bukan langsung dicetak. - Baris 53-56 (
join()semua): threadmainmenunggu keempat pekerja benar-benar selesai, baru stateworkerdibaca ulang — pastiTERMINATED. - Baris 59-61 (
for (String baris : catatan)): seluruh catatan dicetak setelah semua join, sehingga urutan keluaran tidak bergantung kecepatan thread mana pun.
State di Aplikasi Nyata #
Ketika aplikasi produksi mengalami kelambatan, langkah diagnosis klasiknya adalah mengambil dump thread (misalnya dengan jstack <pid>). Tumpukan ratusan thread berlabel BLOCKED pada objek yang sama menunjukkan lock contention — banyak thread berebut satu kunci; gundukan thread WAITING pada antrian internal bisa berarti pekerjaan habis atau notifikasi yang tidak pernah datang; thread TIMED_WAITING pada pemanggilan sleep() yang panjang sering menandakan polling yang seharusnya diganti mekanisme menunggu yang tepat. Membaca pola-pola tersebut adalah keterampilan yang tumbuh dari pemahaman diagram transisi di atas.
State NEW dan TERMINATED mengingatkan kita bahwa Thread hanyalah objek Java biasa yang kehidupan eksekusinya pendek dan sekali jalan. Dua properti objek lainnya — prioritas dan status daemon — mengubah cara JVM memperlakukan thread meski tanpa mengubah diagram transisi. Keduanya menjadi topik Chapter berikutnya.
Referensi #
- Oracle: Enum Thread.State (Java API Specification)
- Oracle: Class Thread (Java API Specification)
- Oracle: The Java Tutorials — Thread States
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
SiklusState.java
Unduh berkaspublic class SiklusState {
private static final Object kunci = new Object();
public static void main(String[] args) throws InterruptedException {
String[] catatan = new String[5];
// sleeper: tidur 300 ms, selama itu state-nya TIMED_WAITING.
Thread sleeper = new Thread(() -> {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "sleeper");
// worker: memegang lock "kunci" sambil tidur 400 ms.
Thread worker = new Thread(() -> {
synchronized (kunci) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "worker");
// waiter: menunggu thread sleeper selesai lewat join tanpa batas waktu.
Thread waiter = new Thread(() -> {
try {
sleeper.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "waiter");
// contender: akan mengantre pada lock "kunci" yang sedang dipegang worker.
Thread contender = new Thread(() -> {
synchronized (kunci) {
// masuk ke sini hanya setelah worker melepas kunci
}
}, "contender");
catatan[0] = "sebelum start : " + worker.getState(); // NEW
sleeper.start();
worker.start();
Thread.sleep(50); // beri waktu worker mengambil kunci dan sleeper mulai tidur
waiter.start();
contender.start();
Thread.sleep(100); // beri waktu contender mengantre dan waiter menunggu
catatan[1] = "sedang tidur : " + sleeper.getState(); // TIMED_WAITING
catatan[2] = "menunggu join : " + waiter.getState(); // WAITING
catatan[3] = "mengantre kunci : " + contender.getState(); // BLOCKED
sleeper.join();
waiter.join();
contender.join();
worker.join();
catatan[4] = "setelah selesai : " + worker.getState(); // TERMINATED
for (String baris : catatan) {
System.out.println(baris);
}
}
}