Đa luồng trong Hướng dẫn Java với các ví dụ

Bất kỳ ứng dụng nào cũng có thể có nhiều quy trình (phiên bản). Mỗi quy trình này có thể được chỉ định dưới dạng một luồng hoặc nhiều luồng. Chúng ta sẽ xem trong hướng dẫn này cách thực hiện nhiều tác vụ cùng lúc và cũng tìm hiểu thêm về các luồng và đồng bộ hóa giữa các luồng.

Trong hướng dẫn này, chúng ta sẽ học:

  • Chủ đề đơn là gì
  • Đa luồng trong Java là gì?
  • Vòng đời của chuỗi trong Java
  • Đồng bộ hóa chuỗi Java
  • Ví dụ về đa luồng trong Java

Chủ đề đơn là gì?

Một luồng đơn về cơ bản là một đơn vị xử lý nhẹ và nhỏ nhất. Java sử dụng các luồng bằng cách sử dụng một "Lớp luồng".

Có hai loại luồng - luồng người dùng và luồng daemon ( luồng daemon được sử dụng khi chúng ta muốn dọn dẹp ứng dụng và được sử dụng ở chế độ nền).

Khi một ứng dụng bắt đầu lần đầu tiên, luồng người dùng sẽ được tạo. Đăng điều đó, chúng ta có thể tạo nhiều luồng người dùng và luồng daemon.

Ví dụ về chuỗi đơn:

gói demo;lớp công khai GuruThread{public static void main (String [] args) {System.out.println ("Một luồng");}}

Ưu điểm của luồng đơn:

  • Giảm chi phí trong ứng dụng khi một luồng thực thi trong hệ thống
  • Ngoài ra, nó làm giảm chi phí bảo trì của ứng dụng.

Đa luồng trong Java là gì?

MULTITHREADING trong Java là một quá trình thực thi đồng thời hai hoặc nhiều luồng để sử dụng tối đa CPU. Các ứng dụng đa luồng thực thi hai hoặc nhiều luồng chạy đồng thời. Do đó, nó còn được gọi là Concurrency trong Java. Mỗi sợi chạy song song với nhau. Nhiều luồng không phân bổ vùng bộ nhớ riêng biệt, do đó chúng tiết kiệm bộ nhớ. Ngoài ra, việc chuyển đổi ngữ cảnh giữa các luồng mất ít thời gian hơn.

Ví dụ về Đa luồng:

gói demo;public class GuruThread1 triển khai Runnable{public static void main (String [] args) {Thread guruThread1 = new Thread ("Guru1");Thread guruThread2 = new Thread ("Guru2");guruThread1.start ();guruThread2.start ();System.out.println ("Các tên luồng sau:");System.out.println (guruThread1.getName ());System.out.println (guruThread2.getName ());}@Ghi đèpublic void run () {}}

Ưu điểm của đa luồng:

  • Người dùng không bị chặn vì các chuỗi là độc lập và chúng tôi có thể thực hiện nhiều hoạt động cùng lúc
  • Vì các luồng này là độc lập, các luồng khác sẽ không bị ảnh hưởng nếu một luồng gặp một ngoại lệ.

Vòng đời của chuỗi trong Java

Vòng đời của một chủ đề:

Có nhiều giai đoạn khác nhau trong vòng đời của luồng như trong sơ đồ trên:

  1. Mới
  2. Runnable
  3. Đang chạy
  4. Đang chờ đợi
  5. Đã chết
  1. Mới: Trong giai đoạn này, luồng được tạo bằng lớp "Lớp luồng". Nó vẫn ở trạng thái này cho đến khi chương trình bắt đầu luồng. Nó còn được gọi là sợi chỉ sinh.
  2. Runnable: Trong trang này, phiên bản của luồng được gọi bằng một phương thức bắt đầu. Điều khiển luồng được trao cho bộ lập lịch để kết thúc quá trình thực thi. Nó phụ thuộc vào bộ lập lịch, có chạy luồng hay không.
  3. Đang chạy: Khi luồng bắt đầu thực thi, sau đó trạng thái được chuyển thành trạng thái "đang chạy". Bộ lập lịch biểu chọn một luồng từ nhóm luồng và nó bắt đầu thực thi trong ứng dụng.
  4. Đang chờ: Đây là trạng thái khi một luồng phải chờ. Vì có nhiều luồng đang chạy trong ứng dụng, nên cần có sự đồng bộ hóa giữa các luồng. Do đó, một luồng phải đợi cho đến khi luồng kia được thực thi. Do đó, trạng thái này được gọi là trạng thái chờ đợi.
  5. Chết: Đây là trạng thái khi luồng bị kết thúc. Luồng đang ở trạng thái đang chạy và ngay sau khi hoàn thành xử lý, nó ở "trạng thái chết".

Một số phương pháp thường được sử dụng cho chủ đề là:

phương pháp Sự miêu tả
khởi đầu() Phương thức này bắt đầu thực thi luồng và JVM gọi phương thức run () trên luồng.
Ngủ (phần nghìn giây) Phương thức này làm cho luồng ở chế độ ngủ, do đó quá trình thực thi của luồng sẽ tạm dừng trong mili giây được cung cấp và sau đó, luồng lại bắt đầu thực thi. Điều này giúp đồng bộ hóa các chủ đề.
getName () Nó trả về tên của chủ đề.
setP priority (thâm niên mới) Nó thay đổi mức độ ưu tiên của luồng.
năng suất () Nó khiến luồng hiện tại tạm dừng và các luồng khác thực thi.

Ví dụ: Trong ví dụ này, chúng ta sẽ tạo một luồng và khám phá các phương thức tích hợp sẵn có cho các luồng.

gói demo;public class thread_example1 triển khai Runnable {@Ghi đèpublic void run () {}public static void main (String [] args) {Thread guruthread1 = new Thread ();guruthread1.start ();thử {guruthread1.sleep (1000);} catch (InterruptException e) {// VIỆC CẦN LÀM Khối bắt được tạo tự độnge.printStackTrace ();}guruthread1.setPosystem (1);int guruposystem = guruthread1.getPosystem ();System.out.println (ngoại lệ);System.out.println ("Đang chạy chuỗi");}}

Giải thích mã:

  • Dòng mã 2: Chúng tôi đang tạo một lớp "thread_Example1" đang triển khai giao diện Runnable (nó phải được triển khai bởi bất kỳ lớp nào có các cá thể dự định được thực thi bởi luồng.)
  • Dòng mã 4: Nó ghi đè phương thức run của giao diện runnable vì bắt buộc phải ghi đè phương thức đó
  • Dòng mã 6: Ở đây chúng ta đã xác định phương thức chính mà chúng ta sẽ bắt đầu thực thi luồng.
  • Dòng mã 7: Ở đây chúng tôi đang tạo một tên luồng mới là "guruthread1" bằng cách khởi tạo một lớp mới của luồng.
  • Dòng mã 8: chúng ta sẽ sử dụng phương thức "bắt đầu" của luồng bằng thể hiện "guruthread1". Tại đây luồng sẽ bắt đầu thực thi.
  • Dòng mã 10: Ở đây chúng tôi đang sử dụng phương thức "ngủ" của luồng bằng cá thể "guruthread1". Do đó, chuỗi sẽ ngủ trong 1000 mili giây.
  • Mã 9-14: Ở đây chúng tôi đã đặt phương thức ngủ trong khối try catch vì có một ngoại lệ đã được kiểm tra xảy ra tức là ngoại lệ bị gián đoạn.
  • Dòng mã 15: Ở đây chúng tôi đang đặt mức độ ưu tiên của luồng thành 1 từ bất kỳ mức độ ưu tiên nào của nó
  • Dòng mã 16: Ở đây chúng ta đang nhận mức độ ưu tiên của luồng bằng cách sử dụng getPosystem ()
  • Dòng mã 17: Ở đây chúng tôi đang in giá trị được lấy từ getPosystem
  • Dòng mã 18: Ở đây chúng tôi đang viết một văn bản mà luồng đang chạy.

Khi bạn thực thi đoạn mã trên, bạn nhận được kết quả sau:

Đầu ra:

5 là mức độ ưu tiên của Thread, và Thread Running là văn bản là đầu ra của mã của chúng ta.

Đồng bộ hóa chuỗi Java

Trong đa luồng, có hành vi không đồng bộ của các chương trình. Nếu một luồng đang ghi một số dữ liệu và một luồng khác đang đọc dữ liệu cùng một lúc, có thể tạo ra sự không nhất quán trong ứng dụng.

Khi có nhu cầu truy cập tài nguyên được chia sẻ bởi hai hoặc nhiều luồng, thì phương pháp đồng bộ hóa được sử dụng.

Java đã cung cấp các phương thức đồng bộ hóa để thực hiện hành vi đồng bộ hóa.

Trong cách tiếp cận này, một khi luồng đi đến bên trong khối được đồng bộ hóa, thì không luồng nào khác có thể gọi phương thức đó trên cùng một đối tượng. Tất cả các luồng phải đợi cho đến khi luồng đó hoàn thành khối được đồng bộ hóa và thoát ra khỏi đó.

Bằng cách này, việc đồng bộ hóa sẽ giúp ích cho một ứng dụng đa luồng. Một luồng phải đợi cho đến khi luồng khác kết thúc quá trình thực thi của nó thì các luồng khác mới được phép thực thi.

Nó có thể được viết dưới dạng sau:

Được đồng bộ hóa (đối tượng){// Khối câu lệnh được đồng bộ hóa}

Ví dụ về đa luồng trong Java

Trong ví dụ này, chúng ta sẽ lấy hai luồng và lấy tên của luồng.

Ví dụ 1:

GuruThread1.javagói demo;public class GuruThread1 triển khai Runnable {/ *** @param args* /public static void main (String [] args) {Thread guruThread1 = new Thread ("Guru1");Thread guruThread2 = new Thread ("Guru2");guruThread1.start ();guruThread2.start ();System.out.println ("Các tên luồng sau:");System.out.println (guruThread1.getName ());System.out.println (guruThread2.getName ());}@Ghi đèpublic void run () {}}

Giải thích mã:

  • Dòng mã 3: Chúng tôi đã sử dụng một lớp "GuruThread1" để triển khai Runnable (nó phải được triển khai bởi bất kỳ lớp nào có các cá thể dự định được thực thi bởi luồng.)
  • Dòng mã 8: Đây là phương thức chính của lớp
  • Dòng mã 9: Ở đây chúng ta đang khởi tạo lớp Thread và tạo một cá thể có tên là "guruThread1" và tạo một luồng.
  • Dòng mã 10: Ở đây chúng ta đang khởi tạo lớp Thread và tạo một cá thể có tên là "guruThread2" và tạo một luồng.
  • Dòng mã 11: Chúng tôi đang bắt đầu luồng tức là guruThread1.
  • Dòng mã 12: Chúng tôi đang bắt đầu luồng tức là guruThread2.
  • Dòng mã 13: Xuất văn bản là "Tên chủ đề sau:"
  • Dòng mã 14: Lấy tên của luồng 1 bằng phương thức getName () của lớp luồng.
  • Dòng mã 15: Lấy tên của luồng 2 bằng phương thức getName () của lớp luồng.

Khi bạn thực thi đoạn mã trên, bạn nhận được kết quả sau:

Đầu ra:

Tên chuỗi đang được xuất ở đây dưới dạng

  • Guru1
  • Guru2

Ví dụ 2:

Trong ví dụ này, chúng ta sẽ tìm hiểu về việc ghi đè các phương thức run () và start () của một giao diện chạy được và tạo hai luồng của lớp đó và chạy chúng tương ứng.

Ngoài ra, chúng tôi đang tham gia hai lớp học,

  • Một trong đó sẽ triển khai giao diện chạy được và
  • Một cái khác sẽ có phương thức chính và thực thi tương ứng.
gói demo;lớp công khai GuruThread2 {public static void main (String [] args) {// VIỆC CẦN LÀM Sơ khai phương thức được tạo tự độngGuruThread3 threadguru1 = new GuruThread3 ("guru1");threadguru1.start ();GuruThread3 threadguru2 = new GuruThread3 ("guru2");threadguru2.start ();}}class GuruThread3 triển khai Runnable {Thread guruthread;tên guruname private String;GuruThread3 (Tên chuỗi) {guruname = tên;}@Ghi đèpublic void run () {System.out.println ("Luồng đang chạy" + guruname);for (int i = 0; i <4; i ++) {System.out.println (i);System.out.println (guruname);thử {Thread.sleep (1000);} catch (InterruptException e) {System.out.println ("Luồng đã bị gián đoạn");}}}public void start () {System.out.println ("Luồng bắt đầu");if (guruthread == null) {guruthread = new Thread (this, guruname);guruthread.start ();}}}

Giải thích mã:

  • Dòng mã 2: Ở đây chúng ta đang lấy một lớp "GuruThread2" sẽ có phương thức chính trong đó.
  • Dòng mã 4: Ở đây chúng ta đang sử dụng một phương thức chính của lớp.
  • Dòng mã 6-7: Ở đây chúng tôi đang tạo một thể hiện của lớp GuruThread3 (được tạo ở các dòng bên dưới của mã) là "threadguru1" và chúng tôi đang bắt đầu luồng.
  • Dòng mã 8-9: Ở đây chúng tôi đang tạo một phiên bản khác của lớp GuruThread3 (được tạo ở các dòng bên dưới của mã) là "threadguru2" và chúng tôi đang bắt đầu luồng.
  • Dòng mã 11: Ở đây chúng tôi đang tạo một lớp "GuruThread3" đang triển khai giao diện có thể chạy được (nó phải được triển khai bởi bất kỳ lớp nào có các cá thể dự định được thực thi bởi luồng.)
  • Dòng mã 13-14: chúng tôi đang lấy hai biến lớp từ đó một biến thuộc loại luồng và biến khác thuộc lớp chuỗi.
  • Dòng mã 15-18: chúng tôi đang ghi đè phương thức khởi tạo GuruThread3, lấy một đối số là kiểu chuỗi (là tên luồng) được gán cho guruname biến lớp và do đó tên của luồng được lưu trữ.
  • Dòng mã 20: Ở đây chúng tôi đang ghi đè phương thức run () của giao diện runnable.
  • Dòng mã 21: Chúng tôi đang xuất tên luồng bằng câu lệnh println.
  • Dòng mã 22-31: Ở đây chúng ta đang sử dụng vòng lặp for với bộ đếm được khởi tạo bằng 0 và nó không được nhỏ hơn 4 (chúng ta có thể lấy bất kỳ số nào do đó vòng lặp ở đây sẽ chạy 4 lần) và tăng bộ đếm. Chúng tôi đang in tên luồng và cũng đặt luồng ở chế độ ngủ trong 1000 mili giây trong khối try-catch khi phương thức sleep đã kiểm tra ngoại lệ.
  • Dòng mã 33: Ở đây chúng tôi đang ghi đè phương thức bắt đầu của giao diện chạy được.
  • Dòng mã 35: Chúng tôi đang xuất ra văn bản "Luồng bắt đầu".
  • Dòng mã 36-40: Ở đây chúng ta đang sử dụng điều kiện if để kiểm tra xem biến lớp guruthread có giá trị trong đó hay không. Nếu nó null thì chúng ta đang tạo một thể hiện bằng cách sử dụng lớp luồng lấy tên làm tham số (giá trị được gán trong hàm tạo). Sau đó, luồng được bắt đầu bằng phương thức start ().

Khi bạn thực thi đoạn mã trên, bạn nhận được kết quả sau:

Đầu ra :

Có hai chủ đề do đó, chúng tôi nhận được hai lần thông báo "Chủ đề đã bắt đầu".

Chúng tôi nhận được tên của chuỗi khi chúng tôi đã xuất chúng.

Nó đi vào vòng lặp for, nơi chúng ta đang in bộ đếm và tên luồng và bộ đếm bắt đầu bằng 0.

Vòng lặp thực hiện ba lần và ở giữa chuỗi được ngủ trong 1000 mili giây.

Do đó, đầu tiên, chúng ta nhận được guru1 rồi đến guru2 rồi lại guru2 vì chuỗi này ngủ ở đây trong 1000 mili giây và tiếp theo là guru1 và lại là guru1, chuỗi sẽ ngủ trong 1000 mili giây, vì vậy chúng tôi nhận được guru2 và sau đó là guru1.

Tóm tắt :

Trong hướng dẫn này, chúng ta đã xem các ứng dụng đa luồng trong Java và cách sử dụng đơn và đa luồng.

  • Trong đa luồng, người dùng không bị chặn vì các luồng độc lập và có thể thực hiện nhiều hoạt động cùng lúc
  • Các giai đoạn khác nhau của vòng đời của luồng là,
    • Mới
    • Runnable
    • Đang chạy
    • Đang chờ đợi
    • Đã chết
  • Chúng tôi cũng đã tìm hiểu về đồng bộ hóa giữa các luồng, giúp ứng dụng chạy trơn tru.
  • Đa luồng làm cho nhiều tác vụ ứng dụng dễ dàng hơn.

thú vị bài viết...