ว่าด้วยเรื่อง “วงจรชีวิตของ Thread” ใน Java
สำหรับเรื่อง Concurrency ใน Java นั้น Thread เป็นส่วนสำคัญมาก แต่ก็อาจจะมีคนสงสัยว่า “วงจรชีวิต” ของ Thread นั้นมันเป็นอย่างไรกันน๊าาา บทความนี้จึงจะมาพูดถึงวงจรชีวิตของ Thread ในภาษา Java กันครับ
เรามาเริ่มต้นกับรูปของวงจรชีวิตของ Thread กันว่าตั้งแต่เกิดจนตายนั้นเป็นอย่างไร
สถานะของ Thread
คลาส java.lang.Thread
จะระบุสถานะของ thread ด้วย enum ชื่อว่า State ซึ่งเป็นค่าที่บอกว่าตอนนี้ thread มีสถานะเป็นอะไร ดังนี้
- NEW — สถานะตอนสร้าง Thread ใหม่เลยและยังไม่ให้ thread เริ่มทำงาน
- RUNNABLE — สถานะของทั้งกำลังทำงานอยู่ (running) หรือพร้อมที่จะทำงาน (ready) แล้วแต่ไม่ใช่สถานะตอนรอทรัพยากรของระบบ
- BLOCKED — สถานะที่ถูกล็อกเพื่อจะเข้าสู่ synchronized block/method
- WAITING — สถานะที่รอ thread อื่นทำงานบางอย่างให้เสร็จก่อนโดยไม่ได้จำกัดระยะเวลาว่าให้ thread นั้นทำงานนานเท่าไร
- TIMED_WAITING — สถานะที่รอ thread อื่นทำงานบางอย่างให้เสร็จก่อนโดยที่ระบุระยะเวลาว่าให้ thread อื่นทำงานนานเท่าไร
- TERMINATED — สถานะที่ thread นั้นทำงานเรียบร้อยแล้ว
ซึ่งต่อไปจะมาดูรายละเอียดของแต่ละสถานะว่าทำงานอย่างไร
New
เป็นขั้นตอนการสร้าง thread ใหม่ ซึ่ง thread จะถูกสร้างแต่ยังไม่ได้เริ่มทำงาน ซึ่งมันจะยังคงสถานะนี้จนกว่า method start()
จะถูกเรียก
สามารถสร้าง thread t ได้ด้วย keyword new คลาส Thread ซึ่งรับ argument เป็นคลาส Runnable ซึ่งคลาส HelloRunnable ได้ implement interface Runnable แล้วถือว่าเป็น type เดียวกัน และตรวจสอบสถานะของ thread t ด้วย getState() จะได้สถานะเป็น NEW
public class HelloThread {
public static void main(String[] args) {
Thread t = new Thread(new HelloRunnable());
System.out.println(t.getState());
}
}
class HelloRunnable implements Runnable {
@Override
public void run() {
}
}
แสดงสถานะของ thread t ดัวย method getState()
NEW
Runnable
เมื่อเราสร้าง thread และ call method start()
ก็จะเปลี่ยนสถานะจาก NEW ไปเป็น RUNNABLE เป็นสถานะของทั้งกำลังทำงานอยู่ (running) หรือพร้อมที่จะทำงาน (ready) แล้วแต่ไม่ใช่สถานะตอนรอทรัพยากรของระบบ
ใน multi-threaded environment, Thread-Scheduler ซึ่งเป็นส่วนหนึ่งของ JVM จะอนุญาตช่วงเวลาหนึ่งสำหรับแต่ละ thread ดังนั้นมันจะทำงานในช่วงเวลาในเวลาหนึ่งมากกว่าที่ปล่อยให้ความคุม RUNNABLE thread อื่น
สามารถรัน thread ได้ด้วยการใช้ method start()
ดังนี้
public class HelloThread {
public static void main(String[] args) {
Thread t = new Thread(new HelloRunnable());
t.start();
System.out.println(t.getState());
}
}
class HelloRunnable implements Runnable {
@Override
public void run() {
}
}
แสดงสถานะของ thread ดัวย method getState()
RUNNABLE
Blocked
Thread จะมีสถานะเป็น BLOCKED ก็ต่อเมื่อมันไม่มีสิทธิทำงาน ซึ่งมันจะเข้าสู่สถานะนี้เมื่อรอสำหรับ monitor lock และพยายาม access เข้าไปหาโค้ดที่ถูกล็อกด้วย thread อื่น
โดยโค้ดตัวอย่างทำงานด้วยการสร้าง thread มา 2 ตัวคือ t1 และ t2 ด้วยคลาส HelloRunnable ซึ่ง มี synchronize method ที่เหมือนเป็นทรัพยากรร่วมกันและทำงาน infinity และให้เทรดทั้งสองทำงานด้วย t1.start()
และ t2.start()
หลังจากนั้น sleep main thread 1 วินาที และตรวจสอบสถานะ t1 เป็น RUNNABLE และสถานะของ t2
เป็น BLOCKED เพราะว่า thread t1
ทำงานและเรียก synchronize method ที่ใช้ทรัพยากรร่วมอยู่ (ซึ่ง while(true) คือทำงานตลอดไป) ดังนั้น thread t2
จึงอยู่ในสถานะ BLOCKED เพราะพยายามใช้ทรัพยากรร่วม
public class HelloThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new HelloRunnable());
Thread t2 = new Thread(new HelloRunnable());
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println(t1.getState());
System.out.println(t2.getState());
}
}
class HelloRunnable implements Runnable {
@Override
public void run() {
commonResource();
}
public static synchronized void commonResource() {
while (true); // Infinity loop
}
}
จะได้ผลลัพธ์ดังนี้
RUNNABLE
BLOCKED
Waiting
Thread จะมีสถานะเป็น WAITING ก็ต่อเมื่อมันรอ thread อื่นทำงานบางอย่างให้เสร็จก่อน ซึ่ง thread สามารถเข้าสู่สถานะนี้ได้ถ้าเรียก method เหล่านี้
- Object.wait ไม่ต้องมี timeout
- thread.join ไม่ต้องมี timeout
- LockSupport.park
โดยโค้ดตังอย่างทำงานดังนี้
- สร้าง thread t1 ใน HelloThread ที่ implement interface Runnable และให้ thread t1 ทำงานด้วย
start()
- ใน method run() ของ thread t1 สร้าง thread
t2
และให้ threadt2
ทำงาน หลังจากนั้น callt2.joint()
เพื่อให้ threadt1
อยู่ในสถานะ WAITING เพื่อให้ threadt2
ทำงานเสร็จก่อนโดยไม่กำหนดเวลา - โดยใน thread
t2
ทำงานตรวจสอบสถานะของ threadt1
ด้วยgetState()
แสดงบนหน้าจอ
public class HelloThread implements Runnable {
public static Thread t1;
public static void main(String[] args) throws InterruptedException {
t1 = new Thread(new HelloThread());
t1.start();
}
@Override
public void run() {
Thread t2 = new Thread(new HelloRunnable());
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread is interrupted");
}
}
}
class HelloRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread interrupted");
}
System.out.println(HelloThread.t.getState());
}
}
จะได้ผลลัพธ์ดังนี้
WAITING
Timed Waiting
Thread จะมีสถานะเป็น TIMED_WAITING ก็ต่อเมื่อ thread รอให้ thread อื่นทำงานบางอย่างโดยระบุเวลา ซึ่ง thread สามมารถเข้าสู่สถานะนี้ได้ถ้าเรียก method เหล่านี้
- Thread.sleep
- Object.wait มี timeout
- Thread.join มี timeout
- LockSupport.partNanos
- LockSupport.partUntil
โดยโค้ดตัวอย่างทำงานด้วยการสร้าง thread t และให้ทำงานด้วย start()
ซึ่งใน thread t1
จะเรียก Thread.sleep()
เพื่อหยุดดการทำงานของ thread เวลา 5 วินาที และใน main thread ก็ตรวจสอบสถานะของ thread t1
จะเห็นได้ว่ามีสถานะ TIMED_WAITING ซึ่งเป็นผลมาจาก thread t1
เรียก sleep()
public class HelloThread {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new HelloRunnable());
t.start();
Thread.sleep(1000);
System.out.println(t.getState());
}
}
class HelloRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread interrupted");
}
}
}
จะได้ผลลัยธ์ดังนี่
TIMED_WAITING
Terminate
สถานะนี้เป็นสถานะที่ thread หยุดการทำงาน ซึ่งสถานะ TERMINATED เมื่อ thread หยุดการทำงานหรือถูกยุติการทำงาน
โดยโค้ดตัวอย่างทำงานง่ายมากด้วยการสร้าง thread t
และให้ thread t
ทำงานด้วย start()
และหยุด main thread 1 วินาที แล้วมาตรวจสอบสถานะของ thread t
จะเห็นได้ว่า thread ทำงานเสร็จแล้วมีสถานะเป็น TERMINATED
public class HelloThread {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new HelloRunnable());
t.start();
Thread.sleep(1000);
System.out.println(t.getState());
}
}
class HelloRunnable implements Runnable {
@Override
public void run() {
}
}
จะได้ผลลัพธ์ดังนี้
TERMINATED