0

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 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.

Tài liệu tham khảo


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí