CodeGym /Blog Java /Ngẫu nhiên /Đa luồng: Các phương thức của lớp Thread làm gì

Đa luồng: Các phương thức của lớp Thread làm gì

Xuất bản trong nhóm
CHÀO! Hôm nay chúng ta sẽ tiếp tục nói về đa luồng. Hãy xem xét lớp Thread và chức năng của một vài phương thức của nó. Khi chúng ta nghiên cứu các phương thức của lớp trước đây, chúng ta thường chỉ viết như sau: <tên phương thức> -> <phương thức làm gì>. Đa luồng: Các phương thức của lớp Thread làm gì - 1Điều này sẽ không hoạt động với Threadcác phương thức của :) Chúng có logic phức tạp hơn mà bạn sẽ không thể hiểu được nếu không có một vài ví dụ.

Phương thức Thread.start()

Hãy bắt đầu bằng cách lặp lại chính mình. Như bạn có thể nhớ lại, bạn có thể tạo một luồng bằng cách làm cho lớp của bạn kế thừa Threadlớp đó và ghi đè run()phương thức. Nhưng nó sẽ không tự khởi động, tất nhiên. Để làm điều này, chúng ta gọi phương thức của đối tượng start(). Đa luồng: Các phương thức của lớp Thread làm gì - 2Hãy nhớ lại ví dụ từ bài học trước:

public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Thread executed: " + getName());
   }
}


public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {
           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Lưu ý: Để bắt đầu một luồng, bạn phải gọistart()phương thức đặc biệt chứ không phảirun()phương thức! Đây là một lỗi dễ mắc phải, đặc biệt là khi bạn mới bắt đầu nghiên cứu về đa luồng. Trong ví dụ của chúng tôi, nếu bạn gọirun()phương thức 10 lần thay vìstart(), bạn sẽ nhận được kết quả như sau:

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {
           MyFirstThread thread = new MyFirstThread();
           thread.run();
       }
   }
}
Nhìn vào kết quả của chương trình của chúng tôi: Chủ đề đã thực hiện: Chủ đề-0 Chủ đề đã thực hiện: Chủ đề-1 Chủ đề đã thực hiện: Chủ đề-2 Chủ đề đã thực hiện: Chủ đề-3 Chủ đề đã thực hiện: Chủ đề-4 Chủ đề đã thực hiện: Chủ đề-5 Chủ đề đã thực hiện: Chủ đề-6 Chủ đề đã thực hiện: Chủ đề-7 Chủ đề đã thực hiện: Chủ đề-8 Chủ đề đã thực hiện: Chủ đề-9 Nhìn vào thứ tự của đầu ra: Mọi thứ đang diễn ra theo thứ tự hoàn hảo. Lạ nhỉ? Chúng tôi không quen với điều này, bởi vì chúng tôi đã biết rằng thứ tự các luồng được bắt đầu và thực thi được xác định bởi một trí tuệ vượt trội bên trong hệ điều hành của chúng tôi: bộ lập lịch luồng. Có lẽ chúng ta chỉ gặp may mắn? Tất nhiên, đây không phải là về may mắn. Bạn có thể xác minh điều này bằng cách chạy chương trình thêm vài lần nữa. Vấn đề là gọirun()phương thức trực tiếp không liên quan gì đến đa luồng. Trong trường hợp này, chương trình sẽ được thực thi trên luồng chính, cùng luồng thực thi phương main()thức. Nó chỉ cần in liên tiếp 10 dòng trên bàn điều khiển và thế là xong. 10 chủ đề chưa được bắt đầu. Vì vậy, hãy nhớ điều này trong tương lai và liên tục kiểm tra bản thân. Nếu bạn muốn run()phương thức được gọi, hãy gọi start(). Hãy đi xa hơn nữa.

Phương thức Thread.sleep()

Để tạm dừng thực thi luồng hiện tại trong một thời gian, chúng tôi sử dụng sleep()phương thức. Đa luồng: Các phương thức của lớp Thread làm gì - 3Phương sleep()thức lấy một số mili giây làm đối số, cho biết lượng thời gian để đưa luồng vào trạng thái ngủ.

public class Main {

   public static void main(String[] args) throws InterruptedException {

       long start = System.currentTimeMillis();

       Thread.sleep(3000);

       System.out.println(" - How long did I sleep? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " seconds");

   }
}
Đầu ra bảng điều khiển: - Tôi đã ngủ bao lâu rồi? - 3 giây Lưu ý: phương sleep()thức tĩnh: nó ngủ luồng hiện tại. Đó là, cái hiện đang được thực hiện. Đây là một điểm quan trọng khác: một chủ đề ngủ có thể bị gián đoạn. Trong trường hợp này, chương trình ném ra một tệp InterruptedException. Chúng ta sẽ xem xét một ví dụ dưới đây. Nhân tiện, điều gì xảy ra sau khi luồng thức dậy? Nó sẽ tiếp tục được thực thi ngay từ nơi nó đã dừng lại? Không. Sau khi một luồng thức dậy, tức là thời gian đã trôi qua như một đối số đã Thread.sleep()trôi qua, nó sẽ chuyển sang trạng thái có thể chạy đượctình trạng. Tuy nhiên, điều này không có nghĩa là bộ lập lịch luồng sẽ chạy nó. Nó hoàn toàn có thể ưu tiên cho một số luồng không ngủ khác và cho phép luồng mới được đánh thức của chúng tôi tiếp tục công việc của nó sau đó một chút. Hãy nhớ kỹ điều này: thức dậy không có nghĩa là tiếp tục công việc ngay lập tức!

Phương thức Thread.join()

Đa luồng: Các phương thức của lớp Thread làm gì - 4Phương join()thức tạm dừng thực thi luồng hiện tại cho đến khi luồng khác kết thúc. Nếu chúng ta có 2 chủ đề, t1t2, và chúng ta viết

t1.join()
sau đó t2sẽ không bắt đầu cho đến khi t1hoàn thành công việc của nó. Phương thức này join()có thể được sử dụng để đảm bảo thứ tự thực hiện của các luồng. Hãy xem xét cách thức join()hoạt động của phương thức trong ví dụ sau:

public class ThreadExample extends Thread {

   @Override
   public void run() {

       System.out.println("Thread started: " + getName());

       try {
           Thread.sleep(5000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("Thread " + getName() + " is finished.");
   }
}


public class Main {

   public static void main(String[] args) throws InterruptedException {

       ThreadExample t1 = new ThreadExample();
       ThreadExample t2 = new ThreadExample();

       t1.start();


 /* The second thread (t2) will start running only after the first thread (t1)
       is finished (or an exception is thrown) */
       try {
           t1.join();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       t2.start();

       // The main thread will continue running only after t1 and t2 have finished
       try {
           t1.join();
           t2.join();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       System.out.println("All threads have finished. The program is finished.");

   }
}
Chúng tôi đã tạo một ThreadExamplelớp đơn giản. Nhiệm vụ của nó là hiển thị thông báo rằng chuỗi đã bắt đầu, chuyển sang chế độ ngủ trong 5 giây và cuối cùng báo cáo rằng công việc đã hoàn tất. Miếng bánh. Logic chính là trong Mainlớp. Xem các nhận xét: chúng tôi sử dụng join()phương pháp này để quản lý thành công thứ tự thực hiện của luồng. Nếu bạn còn nhớ chúng ta đã bắt đầu chủ đề này như thế nào, thứ tự thực hiện được xử lý bởi bộ lập lịch trình luồng. Nó chạy các chủ đề theo ý riêng của mình: mỗi lần theo một cách khác nhau. Ở đây chúng tôi đang sử dụng phương thức để đảm bảo rằng t1luồng sẽ được bắt đầu và thực hiện trước, sau đó làt2luồng và chỉ sau đó luồng chính của chương trình mới tiếp tục. Tiếp tục. Trong các chương trình thực tế, bạn sẽ thường gặp các tình huống khi bạn cần phải ngắt quá trình thực thi của một tiểu trình. Ví dụ: chuỗi của chúng tôi đang chạy nhưng đang chờ một sự kiện hoặc điều kiện nhất định. Nếu nó xảy ra, thì chuỗi dừng lại. Nó có thể có ý nghĩa nếu có một số loại stop()phương pháp. Nhưng nó không đơn giản như vậy. Ngày xửa ngày xưa, Java thực sự đã có một Thread.stop()phương thức và cho phép một luồng bị gián đoạn. Nhưng sau đó nó đã bị xóa khỏi thư viện Java. Bạn có thể tìm thấy nó trong tài liệu của Oracle và thấy rằng nó được đánh dấu là không dùng nữa. Tại sao? Bởi vì nó chỉ dừng luồng mà không làm bất cứ điều gì khác. Ví dụ: chuỗi có thể đang làm việc với dữ liệu và thay đổi điều gì đó. Sau đó, ở giữa công việc của nó, nó đã bị cắt đứt một cách đột ngột và bất thường bởi stop()phương pháp này. Không tắt máy đúng cách, không giải phóng tài nguyên, thậm chí không xử lý lỗi - không có điều này. Để phóng đại một chút, stop()phương pháp này chỉ đơn giản là phá hủy mọi thứ theo cách của nó. Nó giống như kéo dây nguồn ra khỏi ổ cắm để tắt máy tính. Vâng, bạn có thể nhận được kết quả mong muốn. Nhưng mọi người đều biết rằng sau một vài tuần, máy tính sẽ không cảm ơn bạn vì đã đối xử với nó như vậy. Đó là lý do tại sao logic ngắt các luồng đã thay đổi trong Java và hiện sử dụng một interrupt()phương thức đặc biệt.

Phương thức Thread.interrupt()

Điều gì xảy ra nếu interrupt()phương thức được gọi trên một luồng? Có 2 khả năng:
  1. Ví dụ, nếu đối tượng đang ở trạng thái chờ do các phương thức joinhoặc sleep, thì quá trình chờ sẽ bị gián đoạn và chương trình sẽ đưa ra một tệp InterruptedException.
  2. Nếu luồng ở trạng thái hoạt động, thì interruptedcờ boolean sẽ được đặt trên đối tượng.
Nhưng chúng ta phải kiểm tra giá trị của cờ này trên đối tượng và tự mình hoàn thành công việc một cách chính xác! Đó là lý do tại sao Threadlớp có boolean isInterrupted()phương thức. Hãy trở lại với ví dụ về đồng hồ trong một bài học trong khóa học cơ bản. Để thuận tiện, chúng tôi đã đơn giản hóa nó một chút:

public class Clock extends Thread {

   public static void main(String[] args) throws InterruptedException {
       Clock clock = new Clock();
       clock.start();

       Thread.sleep(10000);
       clock.interrupt();
   }

   public void run() {
       Thread current = Thread.currentThread();

       while (!current.isInterrupted())
       {
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               System.out.println("The thread was interrupted");
               break;
           }
           System.out.println("Tick");
       }
   }
}
Trong trường hợp này, đồng hồ được khởi động và bắt đầu tích tắc mỗi giây. Giây thứ 10 ta ngắt kim đồng hồ. Như bạn đã biết, nếu luồng mà chúng tôi đang cố gắng ngắt ở một trong các trạng thái chờ, thì kết quả là tệp InterruptedException. Đây là một ngoại lệ được kiểm tra, vì vậy chúng ta có thể dễ dàng nắm bắt nó và thực hiện logic của mình để kết thúc chương trình. Và đó chỉ là những gì chúng tôi đã làm. Đây là kết quả của chúng ta: Tick Tick Tick Tcik Tcik Tick Tick Tick Tick Chủ đề bị gián đoạn Điều này kết thúc phần giới thiệu của chúng tôi về Threadcác phương thức quan trọng nhất của lớp. Chúc may mắn!
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION