Lập trình đa tuyến trong Java

Với Java ta có thể xây dựng các chưong trình đa luồng. Một ứng dụng có thể bao gồm nhiều luồng. Mỗi luồng được gán một công việc cụ thể, chúng được thực thi đồng thời với các luồng khác.

Có hai cách để tạo ra luồng

Cách 1: Tạo ra một lớp kế thừa từ lớp Thread và ghi đè phương thức run của lớp Thread như sau:

class MyThread extends Thread{
  public void run() {
    //Mã lệnh của tuyến
  }
}

Cách 2: Tạo ra một lớp triển khai từ giao diện Runnable, ghi đè phương thức

```java
class MyThread implements Runnable{
  public void run(){
    //Mã lệnh của tuyến
  }
}

Lớp Thread

Lớp Thread chứa phương thức tạo dựng Thread() cũng như nhiều phương thức hữu ích có chức năng chạy, khởi động, tạm ngừng, tiếp tục, gián đoạn và ngưng tuyến. Để tạo ra và chạy một tuyến ta cần làm 2 bước:

  • Mở rộng lớp Thread và Ghi đè phương thức run()
  • Gọi phương thức start() để bắt đầu thực thi tuyến

Lớp Thread không có nhiều phương thức lắm, chúng chỉ có một vài phương thức hữu dụng được liệt kê sau:

public void run(): được java gọi để thực thi tuyến thi hành, bạn phải ghi đè phương thức này để thực thi nhiệm vụ của tuyến, bởi vì phương thức run()của lớp Thread chỉ là phương thức rỗng

public void start(): khi ta tạo ra tuyến nó chưa thực sự chạy cho đến khi, phương thức start() được gọi, khi start() được gọi thì phương thức run() cũng được kích hoạt

public void stop(): có chức năng ngưng tuyến thi hành, phương thức này không an toàn, bạn nên gán null vào biến Thread để để dừng tuyến, thay vì sử dụng phương thức stop()

public void suspend(): Có chức năng tạm ngừng tuyến, trong java 2, phương thức này ít được sử dụng, bởi vì phương thức này không nhả tài nguyên mà nó lắm giữ, do vậy có thể nguy cơ dẫn đến deadlock ( khoá chết ), bạn nên dùng phương thức wait(), để tạm ngừng tuyến thay vì sử dụng phương thức suspend()

public void resume(): Tiếp tục vận hành tuyến nếu như nó đang bị ngưng, nếu tuyến đang thi hành thì phương thức này bị bỏ qua, thông thường phương thức này được dùng kết hợp với phương thức suspend(), kể từ java 2 phương thức này cùn với phương thức suspend()bị từ chối, do vậy bạn nên dùng phương thức notify () thay vì sử dụng phương thức resume()

public static void sleep( long millis) Threadows InterruptedException: đặt tuyến thi hành vào trạng thái ngủ, trong khoảng thời gian xác định bằng mili giây. chú ý sleep() là phương thức tĩnh.

public void interrupt(): làm gián đoạn tuyến thi hành

public static boolean isInterrupt(): kiểm tra xem tuyến có bị ngắt không

public void setpriority( int p): ấn định độ ưu tiên cho tuyến thi hành, độ ưu tiên được xác định là một số nguyên thuộc đoạn [1,10]

public final void wait() throws InterruptException: đặt tuyến vào trạng thái chờ một tuyến khác, cho đến khi có một tuyến khác thông báo thì nó lại tiếp tục, đây là phương thức của lớp cơ sở Object

public final void notify (): đánh thức tuyến đang chờ, trên đối tượng này

public final void notifyAll(): đánh thức tất cả các tuyến đang chờ trên đối tượng này

isAlive(): Trả về True, nếu luồng là vẫn còn tồn tại (sống)

getPriority(): Trả về mức ưu tiên của luồng

join(): Đợi cho đến khi luồng kết thúc

isDaemon(): Kiểm tra nếu luồng là luồng một luồng chạy ngầm (deamon) setDeamon(boolean on): Đánh dấu luồng như là luồng chạy ngầm

ví dụ: ta tạo ra 2 tuyến thi hành song song, một tuyến thực hiện việc in 200 dòng “Đại học sư phạm kỹ thuật Hưng Yên”, trong khi tuyến này đang thực thi thì có một tuyến khác vẫn tiếp tục in 200 dòng chữ “chào mừng bạn đến với java

public class Hello {
  public static void main ( String[] args ){
    new ChaoDH ().start ();
    new ChaoJV ().start ();
  }
}


class ChaoDH extends Thread{
  public void run () {
    for ( int i = 1; i <= 200; i++ )
    System.out.println ( "Đại học sư phạm kỹ thuật Hưng Yên" );
  }
}


class ChaoJV extends Thread{ public void run () {
  for ( int i = 1; i <= 200; i++ )
    System.out.println ( "chào mừng bạn đến với java" );
  }
}

khi ta chạy chương trình thấy kết quả xen kẽ nhau như

Kết quả
Đại học sư phạm kỹ thuật Hưng Yên
Đại học sư phạm kỹ thuật Hưng Yên chào mừng bạn đến với java
Đại học sư phạm kỹ thuật Hưng Yên chào mừng bạn đến với java
Đại học sư phạm kỹ thuật Hưng Yên chào mừng bạn đến với java
chào mừng bạn đến với java

Vòng đời của Thread

Hình sau thể hiện trạng thái của tuyến trong vòng đời của chúng

image

Luồng chạy ngầm (deamon)

Một chương trình Java kết thúc chỉ sau khi tất cả các luồng thực thi xong. Trong Java có hai loại luồng:

  • Luồng người sử dụng
  • Luồng chạy ngầm (deamon)

Người sử dụng tạo ra các luồng người sử dụng, trong khi các luồng deamon là các luồng chạy nền. Luồng deamon cung cấp các dịch vụ cho các luồng khác. Máy ảo Java thực hiện tiến trình thoát, khi đó chỉ còn duy nhất luồng deamon vẫn còn sống. Máy ảo Java có ít nhất một luồng deamon là luồng “garbage collection” (thu lượm tài nguyên - dọn rác). Luồng dọn rác thực thi chỉ khi hệ thồng không có tác vụ nào. Nó là một luồng có quyền ưu tiên thấp. Lớp luồng có hai phương thức để làm việc với luồng deamon:

  • public void setDaemon(boolean on)
  • public boolean isDaemon()

Giao diện Runnable

Ở mục trước bạn đã tạo ra các luồng thực hiện song song với nhau, trong java ta còn có thể tạo ra các tuyến thi hành song song bằng cách triển khai giao diện Runnable. Chắc bạn sẽ tự hỏi, đã có lớp Thread rồi tại sao lại còn có giao diện Runnable nữa, chúng khác gì nhau?, câu trả lời ở chỗ, java không hỗ trợ kế thừa bội, nếu chương trình của bạn vừa muốn kế thừa từ một lớp nào đó, lại vừa muốn đa tuyến thì bạn bắt buộc phải dùng giao diện Runnable, chẳng hạn như bạn viết các Applet, bạn vừa muốn nó là Applet, lại vừa muốn thực thi nhiều tuyến, thì bạn vừa phải kế thừa từ lớp Applet, nhưng nếu đã kế thừa từ lớp Applet rồi, thì bạn không thể kế thừa từ lớp Thread nữa.

Ta viết lại ví dụ trên, nhưng lần này ta không kế thừa lớp Thread, mà ta triển khai giao diện Runnable

public class Hello {
  public static void main ( String[] args ){
    Thread t = new Thread ( new ChaoDH () );
    t.start ();
    Thread t1 = new Thread ( new ChaoJV () );
    t1.start ();
  }
}

class ChaoDH implements Runnable {
  public void run () {
    ChaoDH thu = new ChaoDH ();
    for ( int i = 1; i <= 200; i++ ) {
      System.out.println("Đại học sư phạm kỹ thuật Hưng Yên");
    }
  }
}

class ChaoJV implements Runnable{
  public void run () {
    for ( int i = 1; i <= 200; i++ ) {
      System.out.println ( "chào mừng bạn đến với java" );
    }
  }
}

Cho chạy ví dụ này ta thấy kết quả ra không khác gì với ví dụ trước

Thiết lập độ ưu tiên cho tuyến

Khi một tuyến được tạo ra, nó nhận một độ ưu tiên mặc định, đôi khi ta muốn điều chỉnh độ ưu tiên của tuyến để đạt được mục đích của ta, thật đơn giản, để đặt độ ưu tiên cho một tuyến ta chỉ cần gọi phương thức setPriority() và truyền cho nó một số nguyên số này chính là độ ưu tiên mà bạn cần đặt.

Ta viết lại ví dụ trên như sau: Thêm vào phương thức main() 2 dòng lệnh:

t.setPriority(1);//Tuyến này có độ ưu tiên là 1
t1.setPriority(10);// Tuyến này có độ ưu tiên là 1

Chạy lại chương trình này sau khi sửa và trước khi sửa ta thấy tuyến t1 được cấp thời gian sử dụng CPU nhiều hơn tuyến t, lý do là ta đã đặt độ ưu tiên của tuyến t1, lớn hơn độ ưu tiên của tuyến t

Chú ý:

  • độ ưu tiên của một tuyến biểu thị bởi một số nguyên nằm trong đoạn từ 1 đến 10, một lỗi sẽ phát sinh nếu ta gán cho nó độ ưu tiên, nằm ngoài khoảng này
  • nếu một tuyến không được đặt độ ưu tiên thì nó sẽ nhận độ ưu tiên mặc định ( bằng 5 ), ta có thể kiểm tra điều này bằng cách gọi phương thức getPriority()

Nhóm tuyến (Thread Group)

Nhóm tuyến là một tập hợp gồm nhiều tuyến, khi ta tác động đến nhóm tuyến ( chẳng hạn như tạm ngưng, …) thì tất cả các tuyến trong nhóm đều nhận được cùng tác động đó, điều này là tiện lợi khi ta muốn quản lý nhiều tuyến thực hiện các tác vụ tương tự nhau.

Để tạo một nhóm tuyến ta cần:

  • tạo ra một nhóm tuyến bằng cách sử dụng phương thức tạo dựng của lớp
ThreadGroup()
ThreadGroup g=new ThreadGroup(ThreadGroupName);
ThreadGroup g=new ThreadGroup(ParentThreadGroup,ThreadGroupName);

Dòng lệnh trên tạo ra một nhóm tuyến g có tên là “ThreadGroupName”, tên của tuyến là một chuỗi và không trùng với tên của một nhóm khác.

  • đưa các tuyến vào nhóm tuyến dùng phương thức tạo dựng của lớp Thread()
Thread =new Thread (g, new ThreadClass(),ThisThread)

Đồng bộ các tuyến thi hành

Khi nhiều tuyến truy cập đồng thời vào tài nguyên dùng chung, mà tài nguyên này lại không thể chia sẻ, cho nhiều tuyến, khi đó tài nguyên dùng chung có thể bị hỏng. Ví dụ, một luồng có thể cố gắng đọc dữ liệu, trong khi luồng khác cố gắng thay đổi dữ liệu. Trong trường hợp này, dữ liệu có thể bị sai.

Trong những trường hợp này, bạn cần cho phép một luồng hoàn thành trọn vẹn tác vụ của nó, và rồi thì mới cho phép các luồng kế tiếp thực thi. Khi hai hoặc nhiều hơn một luồng cần thâm nhập đến một tài nguyên được chia sẻ, bạn cần chắc chắn rằng tài nguyên đó sẽ được sử dụng chỉ bởi một luồng tại một thời điểm.

Bởi trong java không có biến toàn cục, chúng ta chỉ có thuộc tính của đối tượng, tất cả các thao tác có thể dẫn đến hỏng hóc đều thực hiện qua phương thức, do vậy java cung cấp từ khoá synchronized, từ khoá này được thêm vào định nghĩa của phương thức báo cho java biết đây là một phương thức đồng bộ, mỗi đối tượng sẽ có một bộ quản lý khoá, bộ quản lý khoá này chỉ cho 1 phương thức synchronized của đối tượng đó chạy tại một thời điểm

Mấu chốt của sự đồng bộ hóa là khái niệm “monitor” (giám sát), hay còn gọi “semaphore” (cờ hiệu). Một “monitor” là một đối tượng mà được khóa độc quyền. Chỉ một luồng có thể có monitor tại mỗi thời điểm. Tất cả các luồng khác cố gắng thâm nhập vào monitor sẽ bị trì hoãn, cho đến khi luồng đầu tiên thoát khỏi monitor. Các luồng khác được báo chờ đợi monitor. Một luồng có thể monitor một đối tượng nhiều lần.

Avatar Administrator

Administrator

@thaycacac
Ở thực trạng xã hội hiện nay, sự thờ ơ của người tốt còn đáng sợ hơn những thứ xấu xí của xã hội.
Logo thể loại Java

Java

Chuyên mục học lập trình
Học Java từ cơ bản đến thông thạo cho mọi đối tượng
hello