Giải pháp chuyển đổi số trọn gói cho doanh nghiệp Việt
Chào các bạn. Bài viết này ra đời trong bối cảnh có rất nhiều bạn quan tâm đến các bài viết về Thread và đồng bộ hóa của mình, bắt đầu từ Bài 41.
Nếu các bạn có theo dõi các bài viết này, sẽ thấy mình đã bỏ hẳn một bài viết giúp tổng hợp các phương thức hữu ích của Thread. Thật ra nếu bỏ qua việc tìm hiểu các phương thức này, cũng không gây khó khăn cho việc chúng ta đi nhanh qua các bài học sau. Nhưng như vậy thì thật là tiếc vì bản thân Thread có nhiều phương thức khá hay, nếu lúc nào đó bỗng nhiên bạn cần đến, thì việc tìm hiểu thêm về chúng cũng mất kha khá thời gian.
Và cũng nhân tiện có nhiều bạn cũng đặt các câu hỏi xoay quanh một vài phương thức được dùng nhiều, hôm nay mình viết hẳn một bài để tổng hợp lại các phương thức đó của Thread lại. Mình sẽ tập trung giải nghĩa cụ thể vào từng phương thức, có ví dụ rõ ràng, để các bạn nắm rõ hơn và để mình nhanh chóng cho ra những bài viết còn lại của chuỗi kiến thức Thread khá là đồ sộ này nhé.
Lưu ý rằng bài viết hôm nay sẽ chưa có đủ mặt các phương thức, nhưng mình sẽ cập nhật thêm sau này và sẽ để lại đường link đến bài này từ các bài viết liên quan khác.
Một phương thức đơn giản nhưng khá nhiều bạn thắc mắc.
sleep() là một phương thức static của Thread. Do đó chúng ta gọi kèm với tên của lớp: Thread.sleep().
Thread.sleep() làm cho Thread hiện tại (chính là Thread đang gọi đến lệnh Thread.sleep()) phải tạm hoãn lại việc thực thi trong một khoảng thời gian được chỉ định. Nói cho đầy đủ thì là vậy, nhưng các lập trình viên hay dùng theo tên của phương thức, đó là “Ngủ”.
Tuy Thread.sleep() được nạp chồng cho phép bạn chỉ định thời gian ngủ cho Thread hiện tại tính đến nano giây. Nhưng bạn đừng có nghĩ rằng Thread.sleep() sẽ thực sự ngủ với chính xác khoảng thời gian mà bạn định nghĩa nhé. Vì việc ngủ và thức này khiến cho hệ thống phải xử lý và cho ra các độ trễ khác nhau tùy vào thiết bị phần cứng nữa. Do đó Thread.sleep() cũng chỉ tương đối, và chúng ta không bao giờ được dùng Thread.sleep() để xây dựng một chức năng hẹn giờ hay đếm ngược thời gian, điều mà đòi hỏi tính chính xác về mặt thời gian rất cao.
Thread.sleep() được nạp chồng bởi 2 phương thức.
Khi sử dụng Thread.sleep(), bạn phải try catch phương thức này bằng một Checked Exception có tên là InterruptedException. Exception này sẽ được tung ra nếu như có một Thread nào đó khác interrupt Thread này (dừng Thread lại) khi Thread.sleep() đang hoạt động.
Một ví dụ đơn giản, ví dụ này sẽ tin ra console các con số từ 0 đến 5. Mỗi lần in cách nhau nửa giây (500 mili giây). Và vì chúng ta không start() bất cứ Thread nào trong đoạn code dưới, điều đó không có nghĩa rằng là hiện tại ứng dụng đang rỗng không có Thread nào đấy nhé. Bản thân phương thức main() tự nó đã được đưa vào một Thread rồi, gọi là main thread, nên bạn có thể thoải mái gọi đến Thread.sleep() ở đây.
Phương thức join() cho phép một Thread phải chờ cho đến khi các Thread khác kết thúc. Vậy Thread nào phải chờ Thread nào? Vấn đề này khiến nhiều bạn bỡ ngỡ ban đầu, do chỉ có một phương thức join() thì thể hiện thế nào về việc Thread nào chờ Thread nào. Bạn chỉ cần nhớ, Thread nào sử dụng (hay khai báo) các Thread khác, mà một trong các Thread được khai báo này gọi đến phương thức join(), Thread sử dụng sẽ phải đợi Thread khai báo đó kết thúc mới làm tiếp công việc của mình.
join() cũng có các phương thức nạp chồng cho phép Thread hiện tại chỉ cần phải chờ trong một khoảng thời gian tính bằng mili giây hoặc thêm đến nano giây. Và cũng như Thread.sleep(), join() không thực sự đếm thời gian chính xác, nên bạn cũng đừng dùng join() cho một số tình huống đòi hỏi mặt khắt khe về thời gian nhé.
join() được nạp chồng bởi 3 phương thức.
Cũng giống như Thread.sleep(), sử dụng join() cũng cần phải try catch với InterruptedException. Cả hai phương thức này sẽ tung ra Exception này khi được gọi bởi lệnh interrupt.
Chúng ta hãy xây dựng một Thread đơn giản sau đây. Thread này khi chạy sẽ in ra các con số từ 0 đến 5 ở mỗi 0,5 giây.
Còn ở phương thức main() chúng ta sẽ khai báo 3 đối tượng Thread từ lớp MyThread này. Sau khi start thread1 xong thì gọi thread1.join(), sau đó chúng ta cũng sẽ start các Thread còn lại.
Kết quả in ra console như sau.
Bạn có thể thấy, sau khi thread1.start(), thread1 này sẽ bắt đầu in các con số ra console. Tuy nhiên lời gọi thread1.join() sau đó khiến Thread đang sử dụng thread1 này (chính là main thread, thread đang chứa phương thức main() của chúng ta) rơi vào trạng thái đợi cho thread1 hoàn thành, trong quá trình đợi đó, các thread2 và thread3 khi này vẫn chưa được thực thi với hàm start(). Sau khi thread1 đếm xong và hoàn thành nhiệm vụ, các Thread còn lại mới được chạy và bắt đầu đếm là vậy.
Đôi khi chúng ta cần phải ngưng một Thread nào đó đang hoạt động. Cách hợp lý là gọi đến phương thức interrupt() của Thread. Lời gọi interrupt() thực chất cũng không làm cho Thread được gọi đến ngưng tác vụ ngay đâu, điều đó khá là nguy hiểm khi mà hệ thống không biết chắc Thread này đang làm việc gì, biết đâu nó đang ghi một file quan trọng nào đó. Việc một Thread khác interrupt() thoải mái một Thread nào đó có thể gây nên hậu quả khôn lường. Do đó, interrupt() không được xây dựng sẵn các lệnh giúp dừng Thread từ hệ thống, nó chỉ được xem như là một chỉ thị đưa ra cho Thread đang hoạt động biết, tốt hơn hết chính Thread đang hoạt động đó phải biết được chỉ thị này và tự biết cách ngưng tác cụ của mình lại. Một lát ở ví dụ bên dưới chúng ta sẽ nắm được điều này. Hai phương thức còn lại isInterrupted() và Thread.interrupted() dùng để kiểm tra Thread này có bị interrupt hay chưa.
Ví dụ lần này chúng ta hãy đi từ phương thức main(), để xem với lời gọi interrupt() như thế này thì MyThread phải làm sao là đúng nhé.
Nếu như ở MyThread, chúng ta “thờ ơ” với InterruptedException, tức là không có chuẩn bị gì cho sự interrupt cả, như sau.
Kết quả in ra console sẽ như sau. Bạn thấy ở phương thức main() gọi interrupt() trong “vô vọng”, vòng lặp vẫn lặp và đếm như thường, nó chỉ kết thúc khi hoàn thành tác vụ mà thôi.
Tuy nhiên, nếu chúng ta sửa MyThread một chút, có “trách nhiệm” hơn vớ InterruptedException, Thread này sẽ kết thúc dễ dàng do có sự chuẩn bị trước.
Console sẽ in ra như sau.
Hoặc try catch như thế này cũng cho ra kết quả viên mãn như trên.
Lời gọi Thread.currentThread() sẽ nhận về một tham chiếu đến đối tượng Thread hiện tại. Phương thức này cũng không quá phức tạp nên mình cũng không giải thích gì nhiều.
Bạn hãy nhìn vào MyRunnable sau. Trong trường hợp này thì để có thể lấy được tên của Thread đang chạy, chỉ có cách gọi đến Thread.currentThread() mà thôi.
Phương thức main() như sau.
Kết quả in ra console.
Mặc định thì các Thread khi khởi chạy trong ứng dụng, sẽ được hệ thống đặt cho một cái tên, tuần tự, như sau: Thread-0, Thread-1,….
Nếu bạn thấy các tên này rất khó để gợi nhớ, hãy đặt cho chúng một cái tên khác theo ý bạn.
Như bao ví dụ khác, chúng ta cần một Thread khá đơn giản như sau. Thread này sẽ in ra các con số từ 0 đến 5 ở mỗi 0,5 giây. Tuy nhiên Thread có kèm theo phương thức getName() để hiển thị tên của nó ra console.
Ở main() chúng ta sẽ start Thread với cái tên mặc định trước. Đợi Thread này chạy 1 giây sau rồi đổi tên cho nó.
Kết quả in ra console, tên Thread bị đổi sau 2 lần lặp.
Chưa có câu hỏi nào cho bài này.