- vừa được xem lúc

Concurrent and Parallel 006: Vòng đời của một thread diễn ra như thế nào trong Java?

0 0 68

Người đăng: Dat Bui

Theo Viblo Asia

Bài viết nằm trong series Multithread từ hardware tới software với Java.

Các cụ có câu Sinh, lão, bệnh, tử ý muốn nói về quy luật của cuộc đời mà bất cứ ai cũng phải trải qua. Thread cũng không nằm ngoài quy luật đó. Mở bài đủ dài rồi, bắt đầu thôi.

Thread lifecycle

Khi mở một ứng dụng mới, nó sẽ bắt đầu với một thread đầu tiên, được gọi là main thread. Trong Java, main thread sẽ chạy đầu tiên và thực hiện lời gọi tới hàm main(String[] args).

Để tăng tốc chương trình thì main thread sẽ khởi tạo thêm nhiều thread bên trong nó, tất nhiên vẫn thuộc cùng một process và thực hiện các tác vụ độc lập với nhau. Vẫn hơi chậm, các thread bên trong tiếp tục tạo thêm nhiều thread bên trong khác để thực thi công việc (phụ thuộc vào lập trình viên có tạo thread mới không).

Khi một thread được thực thi xong, nó báo cho parent threadkết liễu cuộc đời của mình. Hơi sợ nhỉ, nghỉ hưu có được không? Nghỉ hưu vẫn sẽ tốn chi phí, nên chắc là.. không được. Khắc nghiệt quá.

Với OS, vòng đời của thread sẽ có những state (trạng thái) cơ bản sau:

  • New. Mới khởi tạo, chưa được đưa vào queue.
  • Runnable. Được đưa vào queue, chờ được thực thi.
  • Running. Đang được thực thi bởi processor.
  • Blocked. Bị dừng thực thi vì một vài lý do.
  • Terminated. Về với đất mẹ.

Với mỗi ngôn ngữ lập trình, sẽ có sự thêm bớt các state tuy nhiên vẫn dựa trên 5 state cơ bản trên. Với Java, thread6 state:

  • New
  • Runnable
  • Blocked
  • Waiting
  • Timed waiting
  • Terminated

Cùng đi tìm hiểu kĩ hơn vòng đời của thread trong Java và các state nó phải trải qua.

Từ kết luận ở bài trước, việc thực thi thread sẽ do OS quyết định. Đa số các ngôn ngữ lập trình trong đó có Java không có cơ hội để can thiệp vào. Vì vậy nên với thread trong Java không có state Running.

Ngoài ra, có 2 state mới WaitingTimed waiting, bản chất là tập con của Blocked, tuy nhiên phân chia để làm rõ hơn tình trạng cụ thể của thread.

1) New

Khi khai báo mới một thread, nó sẽ có state New. Lưu ý là chưa start thread. Ví dụ, khai báo thread với Java 11:

final var firstThread = new Thread(() -> System.out.println("First thread"));
final var secondThread = new Thread(() -> System.out.println("Second thread"));

That's easy game.

2) Runnable

Một vài ngôn ngữ lập trình yêu cầu chúng ta bắt buộc phải start thread một cách thủ công trong đó có Java. Chạy dòng code bên dưới:

firstThread.start();
secondThread.start();

Sau khi start thread, nó sẽ được đưa vào ready queue, nhiệm vụ tiếp theo là của OS (bài trước). Chúng ta không biết thread thật sự được thực thi khi nào, thời gian thực thi là bao lâu.

Kết quả có thể là:

First thread
Second thread

hoặc:

Second thread
First thread

Có cách nào để kiểm soát được output không? Chuyển sang single thread thôi ?. Hoặc phải implement một cách khác (lượt bài sau).

3) Waiting

Mình thích những điều thực tế nên lấy ví dụ trước.

Chắc ai cũng đã từng chờ bạn gái (ít thì 30 phút, nhiều thì 1 vài giờ) để đưa đi.. đâu cũng được. Chàng trai (gọi tắt là B) và cô gái (gọi tắt là G), B đến đón G đi chơi cuối tuần.

  • B: Em sắp xong chưa, anh đến rồi!
  • G: Chờ em một chút, em xuống ngay.

Vì chưa có kinh nghiệm nên B chờ mốc mỏ, héo cả người mà không thấy G đâu.

Với ngôn ngữ kĩ thuật, hãy coi BG là 2 thread. G sẽ phát thông báo cho B là chờ một chút. Tuy nhiên do chưa có kinh nghiệm nên B vội vàng tin lời và chờ đợi trong vô vọng.

Như vậy, Waiting là trạng thái thread này chờ thread khác và không có thời gian cụ thể.

Cụ thể với implementation, thread sẽ rơi vào trạng thái Waiting khi gọi các method:

object.wait();
thread.join();
LockSupport.park();

4) Timed waiting

Rút kinh nghiệm lần trước, B đến đón G đi chơi:

  • B: Em sắp xong chưa, anh đến rồi!
  • G: Chờ em một chút, em xuống ngay.
  • B: Chó nó tin, anh chờ em 20 phút thôi, em không xuống anh.. đi về (mạnh mồm thế chứ 2 tiếng cũng phải chờ).

Timed waiting là trạng thái của thread khi chờ thread khác nhưng có thời gian giới hạn. Nếu quá thời gian chờ, thread tiếp tục chạy và không chờ nữa.

Với implementation, Timed waiting xảy ra khi gọi các method sau, tất cả đều có tham số timeout:

Thread.sleep(long);
object.wait(long);
object.wait(long, int);
thread.join(long);
thread.join(long, int);
LockSupport.parkNanos(long);
LockSupport.parkNanos(Object, long);
LockSupport.parkUntil(long);
LockSupport.parkUtil(Object, long);

5) Blocked

Về cơ bản, Blocked khá giống Waiting/Timed waiting. Tuy nhiên nó không diễn tả việc 1 thread chờ các thread còn lại.

Nó nói đến việc khi các thread cùng truy cập vào shared resource, chỉ có duy nhất 1 thread thành công, các thread còn lại rơi vào trạng thái Blocked.

Lấy ví dụ cho sinh động và dễ hiểu.

Bài toán có 4 cô gái và 1 chàng trai lạc trên hoang đảo (đừng nghĩ bậy nhé các bạn). Cả 4 cô gái đều muốn làm quen với chàng trai. Chàng trai sức lực có hạn chỉ tiếp được 1 cô tại một thời điểm (kém quá nhỉ, phải tay mình thì... chắc mình cũng chỉ được thế ?).

Các cô gái đều tỏ ra văn minh lịch sự, đưa ra thỏa thuận với nhau. Lần lượt từng người tham chiến, sau khi xong sẽ đến người tiếp theo.

Với ngôn ngữ kĩ thuật, ta có thể hiểu 4 cô gái là 4 thread cùng muốn truy cập vào nguồn dữ liệu dùng chung (shared resource) là chàng trai (phận con trai 12 bến nước). Tuy nhiên cả 4 thread không Xabi Alonso vào ngay mà nhường nhịn, chờ người trước xong thì mình vào, thực hiện đồng bộ (synchronize) với nhau. Những người chờ bên ngoài sẽ có trạng thái Blocked.

Cụ thể với implementation như thế nào để thread rơi vào Blocked, synchronize là gì, mình sẽ giải thích ở bài tiếp theo. Tạm thời ta chỉ cần hiểu được trạng thái Blocked là gì.

6) Terminated

Vẫn không thoát khỏi quy luật của cuộc đời. Sinh, lão, bệnh tử.

Terminated muốn nói đến trạng thái của thread khi đã hoàn thành xong sứ mệnh và trở về với cõi vĩnh hằng.

Có nhiều cách để terminate, đa số sẽ quy về 2 loại:

  • Già rồi chết. Thread thực hiện xong công việc một cách bình thường và ra đi.
  • Chết bất thình lình. Tại nạn chết, bệnh nhưng không chữa nên chết. Giống như việc có exception và không handle nên thread Terminated.

Bài viết đã terminated. Subcribe để nhận thông báo khi có bài viết mới nhé.

Reference

© Dat Bui

Bình luận

Bài viết tương tự

- vừa được xem lúc

Sử dụng goquery trong golang để crawler thông tin các website Việt Nam bị deface trên mirror-h.org

. Trong bài viết này, mình sẽ cùng mọi người khám phá một package thu thập dữ liệu có tên là goquery của golang. Mục tiêu chính của chương trình crawler này sẽ là lấy thông tin các website Việt Nam bị deface (là tấn công, phá hoại website, làm thay đổi giao diện hiển thị của một trang web, khi người

0 0 237

- vừa được xem lúc

Go Concurrency qua các ví dụ (Phần 1): Dining Philosophers

Bài toán Dining Philosophers (Bữa tối của các triết gia) là một trong những bài toán kinh điển thường dùng để mô tả các vấn đề trong việc xử lý concurrent, những vấn đề thường gặp trong quá trình cấp

0 0 258

- vừa được xem lúc

Concurrent and Parallel 001: Parallel computing hardware P1

Bài viết nằm trong series Concurrent vs Parallel in Java. 1) Sequential vs Parallel computing.

0 0 60

- vừa được xem lúc

005: OS đối xử với thread thế nào?

Bài viết nằm trong series Multithread từ hardware tới software với Java. 1) Schedular.

0 0 42

- vừa được xem lúc

003: Thread và Process

Bài viết nằm trong series Multithread từ hardware tới software với Java. .

0 0 47

- vừa được xem lúc

009: Deadlock, Livelock, Abandoned lock và Starvation

Bài viết nằm trong series Multithread từ hardware tới software với Java. Mình thích ví dụ trực quan, nó dễ hình dung hơn so với ngôn ngữ kĩ thuật khô khan.

0 0 42