THREAD AND THREAD POOLS IN JAVA
1. Tạo và chạy Thread trong Java Có 2 cách tạo thread trong Java: Implement interface Runnable. Extends lớp Thread. a. Tạo thread bằng cách thực thi interface Runnable Khi tạo thread bằng cách thực thi interface Runnable thì ta phải thực thi hàm run(). class RunnableThread implements ...
1. Tạo và chạy Thread trong Java
Có 2 cách tạo thread trong Java:
Implement interface Runnable. Extends lớp Thread.
a. Tạo thread bằng cách thực thi interface Runnable
Khi tạo thread bằng cách thực thi interface Runnable thì ta phải thực thi hàm run().
class RunnableThread implements Runnable{ public RunableThread(){ } pubic RunableThread(String threadName){ Thread thread = new Thread(this, threadName); System.out.println(threadName); thread.start(); } public void run(){ System.out.println(Thread.currentThread()); } }
Ví dụ trên ta đã tạo lớp RunnableThread thực thi interface Runnable, thực thi hàm run(). Giờ ta sẽ khởi tạo và run RunnableThread và xem kết quả.
public class RunnableExecute(){ pubic static void main(String[] args){ Thread thread1 = new Thread(new RunnableThread(), "thread1"); //Tạo mới thread RunnableThread runnableThread = new RunnableThread("Vunv-thread"); //Run thread1 thread1.start(); } }
Output: Vunv-thread Thread[Vunv-thread,5,main] Thread[thread1,5,main]
b. Tạo thread bằng cách kế thừa lớp Thread
Khi tạo thread từ việc kế thừa lớp Thread thì ta phải ghi đè hàm run()
class ExtendsThread extends Thread{ public ExtendsThread(){ } public ExtendsThread(String threadName){ super(threadName); System.out.println(this.getName()); start(); } public void run(){ System.out.println(this.currentThread()); } } public class ExtendsExecute(){ public static void main(String args[]){ Thread thread1 = new ExtendsThread();//new thread with name default ExtendsThread xThread = new ExtendsThread("Vunv-thread"); //run thread thread1.start(); } }
Output:
Vunv-thread
Thread[Vunv-thread,5,main]
Thread[Thread-0,5,main]
2. Sleeping, Interrupts, join with Thread
a. Sleeping
Khi bạn muốn start một thread sau một khoảng thời gian (ms) thì gọi hàm sleep([miniseconds]). Ta xét ví dụ sau.
public class SleepMessages { public static void main(String args[]) throws InterruptedException { String importantInfo[] = { "Mares eat oats", "Does eat oats", "Little lambs eat ivy", "A kid will eat ivy too" }; for (int i = 0; i < importantInfo.length; i++) { //Pause for 4 seconds Thread.sleep(4000); //Print a message System.out.println(importantInfo[i]); } } }
Lưu ý: Hàm main bắn ra exception InteruptedException, exception này do hàm sleep() bắn ra khi có thread khác ngắt thread hiện hành khi hàm sleep() được kích hoạt.
b. Interrupts
Khi bạn muốn ngắt một thread đang chạy, bạn hãy sử dụng hàm interrupted(). Ta xét ví dụ sau.
public class ThreadDemo implements Runnable{ Thread t; ThreadDemo(){ t = newThread(this); System.out.println("Executing "+ t.getName());// this will call run() fucntion t.start();// interrupt the threads //Sleep 2 minutes before interrupt try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } if(!t.interrupted()){ t.interrupt(); }// block until other threads finishtry{ t.join(); }catch(InterruptedException e){}} public void run(){ try{ while(true){ Thread.sleep(1000); } }catch(InterruptedException e){ System.out.print(t.getName()+ " interrupted:"); System.out.println(e.toString()); } } publicstaticvoid main(String args[]){ newThreadDemo(); newThreadDemo(); } }
Output:
Executing Thread-0
1
2
3
Thread-0 interrupted:java.lang.InterruptedException: sleep interrupted
Executing Thread-1
1
2
3
Thread-1 interrupted:java.lang.InterruptedException: sleep interrupted
Vòng lặp vô hạn thực hiện print được 3 lần thì thread bị ngắt. Khi hàm sleep được kích hoạt mà thread bị ngắt thì InterruptedException được bắn ra.
c. Joins
Hàm join() cho phép một thread đợi một thread kết thúc rồi mới thực hiện. Xét ví dụ sau:
class PrintDemo { public void printCount() { try { for (int i = 5; i > 0; i--) { System.out.println("Counter --- " + i); } } catch (Exception e) { System.out.println("Thread interrupted."); } } } //===================================================================== class ThreadDemo extends Thread { private Thread t; private String threadName; PrintDemo PD; ThreadDemo(String name, PrintDemo pd) { threadName = name; PD = pd; } public void run() { PD.printCount(); System.out.println("Thread " + threadName + " exiting."); } public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); } } } //=================================================================================== public class TestThread { public static void main(String args[]) { PrintDemo PD = new PrintDemo(); ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD); ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD); T1.start(); T2.start(); // wait for threads to end try { T1.join(); T2.join(); } catch (Exception e) { System.out.println("Interrupted"); } } }
Output:
Starting Thread - 1
Starting Thread - 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 1 exiting.
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.
Ta thấy thread – 1 thực hiện xong (print từ 5-1) thì thread – 2 bắt đầu thực hiện. Nếu ta không xử dụng hàm join() thì kết quả như sau:
Starting Thread - 1
Starting Thread - 2
Counter --- 5
Counter --- 4
Counter --- 5
Counter --- 3
Counter --- 4
Counter --- 2
Counter --- 3
Counter --- 1
Counter --- 2
Thread Thread - 2 exiting.
Counter --- 1
Thread Thread - 1 exiting.
3. Một vấn đề quan trọng khi sử dụng Thread trong ngôn ngữ lập trình nói chung và trong Java nói riêng, là xử lý đồng bộ và Deadlock
a. Synchronization
Ta xem lại ví dụ trên (mục 2 – c), trong đó lớp TestThread được không sửa dụng join(), như sau:
public class TestThread { public static void main(String args[]) { PrintDemo PD = new PrintDemo(); ThreadDemo T1 = new ThreadDemo("Thread - 1 ", PD); ThreadDemo T2 = new ThreadDemo("Thread - 2 ", PD); T1.start(); T2.start(); // wait for threads to end // try { // T1.join(); // T2.join(); // } catch (Exception e) { // System.out.println("Interrupted"); // } } }
Output:
Starting Thread - 1
Starting Thread - 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 5
Counter --- 2
Counter --- 4
Counter --- 1
Counter --- 3
Thread Thread - 1 exiting.
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.
Giờ ta thực hiện đồng bộ thread để Thread-1 thực hiện xong thì Thread-2 mới thực hiện. Tương tự như hàm join(). Khi đó lớp ThreadDemo như sau.
class ThreadDemo extends Thread { private Thread t; private String threadName; PrintDemo PD; ThreadDemo(String name, PrintDemo pd) { threadName = name; PD = pd; } public void run() { //Gọi hàm printCount() được đồng bộ synchronized(PD){ PD.printCount(); } System.out.println("Thread " + threadName + " exiting."); } public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); } } }
Output:
Starting Thread - 1
Starting Thread - 2
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 1 exiting.
Counter --- 5
Counter --- 4
Counter --- 3
Counter --- 2
Counter --- 1
Thread Thread - 2 exiting.
b. Deadlock
Deadlock trong thread là hiện tượng hai hay nhiều thread đợi nhau vòng tròn, có nghĩa là các thread bị rơi vào tình trạng đợi vô thời hạn.
class ThreadResource1 { public static synchronized void go1() { System.out.println("Entered in resource 1"); System.out.println(Thread.currentThread().getName()); try { Thread.sleep(5000); } catch (Exception e) { } ThreadResource2.go2(); System.out.println("Exiting from ThreadResource1"); } } //============================================================================== class ThreadResource2 { public static synchronized void go2() { System.out.println("Entered in resource 2"); System.out.println(Thread.currentThread().getName()); try { Thread.sleep(5000); } catch (Exception e) { } ThreadResource1.go1(); System.out.println("Exiting from ThreadResource2"); } } //================================================================================== public class RunDeadLock { public static void main(String[] args) { Thread thread1 = new Thread(new Runnable() { @Override public void run() { ThreadResource1.go1(); } } ); Thread thread2 = new Thread(new Runnable() { @Override public void run() { ThreadResource2.go2(); } } ); thread1.start(); thread2.start(); } }
Output:
Entered in resource 1
Entered in resource 2
Thread-0
Thread-1
Hiện tượng deadlock xảy ra, hai Thread-1 và Thread-2 đợi nhau vô hạn và không thể kết thúc. Bằng chứng là hai dòng “Entered in resource 1″ và “Entered in resource 2″ chỉ được print 1 lần, và “Thread-0″, “Thread-1″ đồng thời được print. Vậy làm sao để deadlock không xảy ra? Để ý thấy ThreadResource1.class bị lock trước ThreadResource2.class. Giải pháp là chỉ một hàm được thực hiện và hàm kia phải đợi. Do vậy ta xử lý đồng bộ ở hàm go2() như sau.
public static void go2() { synchronized (ThreadResource1.class) { synchronized (ThreadResource2.class) { System.out.println("Entered in resource 2"); System.out.println(Thread.currentThread().getName()); try { Thread.sleep(500); } catch (Exception e) { } ThreadResource1.go1(); System.out.println("Exiting from resource"); } } }
Output:
Entered in resource 1
Thread-0
Entered in resource 2
Thread-0
Entered in resource 1
Thread-0
Entered in resource 2
Thread-0
Entered in resource 1
Thread-0
Entered in resource 2
Thread-0
Entered in resource 1
Thread-0
Entered in resource 2
Thread-0
Deadlock không xảy ra nữa vì thread1 thực hiện trong khi thread2 luôn phải đợi. Bằng chứng là “Thead-1″ không được print ra.
Vì thời gian có hạn nên việc tìm hiểu và trình bày về Thread pools không thực hiện được. Ở topic sau, tôi sẽ trình bày tiếp về Thread pools.