Thread sleep() là gì?
Thread.sleep()
được sử dụng để tạm ngưng quá trình của thread
hiện tại với một khoảng thời gian được chỉ định. Lưu ý khoảng thời gian tạm ngưng không thể là số âm nếu không sẽ bị ném ra exception IllegalArgumentException
.
Chúng ta có các overloading sleep()
sau:
Thread.sleep(long millis)
– Tạm ngưngthread
hiện tại khoảng millis.Thread.sleep(long milis, long nanos)
– Tạm ngưng thread hiện tại khoảngmillis
và thêm một khoảng nanos từ0
đến999999
.
Thread sleep() hoạt động như thế nào?
Thread.sleep()
sẽ nói chuyện với trình lên lịch của hệ thống để đẩy thread hiện tại và trạng thái chờ với một khoảng thời gian được chỉ định. Khi khoảng thời gian chờ thread
sẽ chuyển từ trạng thái chờ sang trạng thais runnable
và chờ đến khi CPU
thực thi tiếp thread.
Như vậy khoảng thời gian chờ thật sự sẽ phụ thuộc vào trình lên lịch và bộ hẹn giờ của hệ thống. Nếu hệ thống đang rảnh thì khoảng thời gian chờ có thể gần với khoảng thời gian mà chúng ta chỉ định, còn nếu hệ thống đang bận thì khoảng chênh lệch sẽ khá lớn so với khoảng thời gian được chỉ định.
Thread sleep sẽ không làm ảnh hưởng đến thread
hiện tại mà chỉ đơn giản là tạm ngưng thực thi một khoảng thời gian, các cơ chế đồng bộ, các kết quả tính toán sẽ không bị ảnh hưởng. Một thread
đang ngủ có thể bị làm gián đoạn bởi các thread
khác đang chạy, lúc này thread
đang ngủ sẽ thức dậy và ném InterruptedException
, cho nên chúng ta phải xử lý exception cho sleep()
.
Tạm ngưng main thread trong khoảng thời gian 2000ms
java
public class ThreadSleep {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(2000);
System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start));
}
}
Kết quả
Output: Sleep time in ms = 2001
Kết quả trên mình đã chạy nhiều lần mới được, để chúng ta thấy rằng không phải cứ sleep(2000)
là nó chờ đúng 2000ms
đâu nhé.
Tạm ngưng main thread trong khoảng thời gian 2000ms
java
class ThreadSleep {
public static void main(String[] args) throws InterruptedException {
System.out.println("Main thread start");
long startMain = System.currentTimeMillis();
Thread.sleep(1000);
Thread t = new Thread(() -> {
System.out.println("Child thread start");
try {
long start = System.currentTimeMillis();
Thread.sleep(1000);
System.out.println("Child thread:" + (System.currentTimeMillis() - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
System.out.println("Main thread:" + (System.currentTimeMillis() - startMain));
}
}
Kết quả
Main thread start
Main thread:1030
Child thread start
Child thread:1000
Thread join()
là gì?
Phương thức join()
được sử dụng để đảm bảo cho quá trình thực thi của Thread
đang chạy không bị gián đoạn bởi các Thread
khác. Nói một cách khác, nếu một Thread
đang trong quá trình được thực thi thì các Thread
khác sẽ phải chờ đợi cho đến khi Thread
đó thực thi xong. join()
được sử dụng khi trong một chương trình Java có nhiều hơn một Thread
và chúng ta cần đảm bảo các Thread
thực thi và kết thúc đúng theo thứ tự mà chúng đã được khởi tạo.
Trong một chương trình Java thường có nhiều hơn một thread, trong đó có main thread
- có chức năng khởi tạo và kích hoạt để chạy các thread
khác, tuy nhiên các main threa
d không đảm bảo các thread
thực thi và kết thúc theo đúng thứ tự mà chúng đã được khởi chạy.
Khi không sử dụng hàm Join
Ở đây có 3 thread th1, th2, th3
. Mặc dù các thread
được khởi chạy theo thứ tự th1>th2>th3
nhưng kết thúc thực thi của 3 thread trên không theo thứ tự th1>th2>th3
. Ở mỗi thời điển chạy chương trình có thể nhận được các kết quả khác nhau.
java
public class JoinExample {
public static void main(String[]args){
Thread th1 = new Thread(new MyThread(),"th1");
Thread th2 = new Thread(new MyThread(),"th2");
Thread th3 = new Thread(new MyThread(),"th3");
th1.start();
th2.start();
th3.start();
}
}
class MyThread implements Runnable {
public void run() {
Thread t = Thread.currentThread();
System.out.println("Bắt đầu thread: " + t.getName());
try{
Thread.sleep(4000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Kết thúc thread:" + t.getName());
}
}
Kết quả
Bắt đầu thread: th1
Bắt đầu thread: th3
Bắt đầu thread: th2
Kết thúc thread:th3
Kết thúc thread:th1
Kết thúc thread:th2
Khi sử dụng hàm Join
Câu hỏi đưa ra là làm thế nào để các thread thực thi và kết thúc theo đúng thứ tự mà chúng được khởi chạy. Câu trả lời là sử dụng hàm Join sẵn có của Java. Giả sử thứ tự của các thread
là thread1
chạy trước, sau đó là thread2
, thread3
chạy sau cùng. Chương trình cài đặt sử dụng hàm Join như sau:
java
public class JoinExample {
public static void main(String[]args){
Thread th1 = new Thread(new MyThread(),"th1");
Thread th2 = new Thread(new MyThread(),"th2");
Thread th3 = new Thread(new MyThread(),"th3");
//khởi chạy thread 1.
th1.start();
try {
th1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//khởi chạy thread 2.
th2.start();
try {
th2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//khởi chạy thread 3.
th3.start();
try {
th3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread implements Runnable {
public void run() {
Thread t = Thread.currentThread();
System.out.println("Bắt đầu thread: " + t.getName());
try{
Thread.sleep(4000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Kết thúc thread:" + t.getName());
}
}
Kết quả
Bắt đầu thread: th1
Kết thúc thread:th1
Bắt đầu thread: th2
Kết thúc thread:th2
Bắt đầu thread: th3
Kết thúc thread:th3
Khi so sánh với chương trình cài đặt không sử dụng hàm Join
, thread2, thread3
, chưa khởi chạy ngay được gọi bằng lệnh th2.start()
, th3.start()
, thead2
và thread3
đã đợi cho đến khi thread1
thực thi xong mới khởi chạy. Tương tự khi thread2
chạy thì thread3 vẫn phải đợi cho đến khi thread2
thực thi xong. Do đó mà kết quả in ra màn hình theo đúng thứ tự th1>th2>th3
mà chúng đã được khởi chạy.
Ví dụ phương thức join()
public void join()throws InterruptedException
public void join(long milliseconds)throws InterruptedException
về phương thức join()
java
class TestJoinMethod1 extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
try {
Thread.sleep(500);
} catch (Exception e) {
System.out.println(e);
}
System.out.println(i);
}
}
public static void main(String args[]) {
TestJoinMethod1 t1 = new TestJoinMethod1();
TestJoinMethod1 t2 = new TestJoinMethod1();
TestJoinMethod1 t3 = new TestJoinMethod1();
t1.start();
try {
t1.join();
} catch (Exception e) {
System.out.println(e);
}
t2.start();
t3.start();
}
}
Kết quả
1
2
3
4
5
1
1
2
2
3
3
4
4
5
5
Như bạn thấy trong ví dụ trên, khi t1
hoàn thành nhiệm vụ của nó thì t2
và t3
bắt đầu thực thi.
về phương thức join(long miliseconds)
java
class TestJoinMethod2 extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
try {
Thread.sleep(500);
} catch (Exception e) {
System.out.println(e);
}
System.out.println(i);
}
}
public static void main(String args[]) {
TestJoinMethod2 t1 = new TestJoinMethod2();
TestJoinMethod2 t2 = new TestJoinMethod2();
TestJoinMethod2 t3 = new TestJoinMethod2();
t1.start();
try {
t1.join(1500);
} catch (Exception e) {
System.out.println(e);
}
t2.start();
t3.start();
}
}
Kết quả
1
2
3
4
1
1
5
2
2
3
3
4
4
5
5
Trong ví dụ trên, khi t1
hoàn thành nhiệm vụ của nó cho 1500 mili giây (3 lần) thì t2 và t3 bắt đầu thực thi.