Multi-thread là gì? Tất tần tật thông tin cụ thể gửi tới bạn đọc

Mục lục

1. Bạn hiểu Multi-thread là gì?

Trước khi hiểu về Multi-thread thì bạn cần hiểu về Thread là gì. Thread là khái niệm hay thuật ngữ chỉ về luồng trong lập trình. Luồn ở đây tức là một tiến trình nhỏ hay còn gọi là tiến trình con, trong tiếng Anh là Sub-process. Đây được biết đến là một đơn vị nhỏ nhất của máy tính được sử dụng để xử lý và thực hiện một công việc nào đó một cách riêng biệt. Các luồng này được quản lý trực tiếp với máy ảo ở trong Java.

Nếu như Thread là luồng thì Multi-thread có nghĩa là đa luồng. Đây là một tiến trình bao gồm nhiều luồng khác nhau cùng chạy một cách đồng thời trong khoảng thời gian đó. Bạn có thể hiểu là trong một ứng dụng Java thì bên cạnh các luồng chính sẽ có những luồng bên ngoài khác chạy cùng lúc đó để thực thi các công việc khác giúp cho ứng dụng có thể chạy và hoạt động mượt hơn, hiệu quả hơn.

Việc sử dụng Multi-thread sẽ giúp cho việc quản lý các yêu cầu của người dùng với số lượng nhiều hơn mà không cần phải chạy thêm hay sử dụng thêm các phiên bản khác của chương trình đang chạy và thực hiện trong máy tính. 

Ví dụ điển hình và dễ hiểu nhất về multi-thread mà qua đó bạn sẽ giải thích được Multi-thread đơn giản hơn. Đó chính là việc bạn chạy chương trình chơi nhạc. Khi bạn đang thực hiện việc chơi nhạc thì cùng lúc đó bạn vẫn có thể thực hiện các chức năng khác như việc dừng bài, chuyển bài hay quay trở lại bài trước,... Điều này thực hiện được là do luồng nhạc và luồng tiếp nhận các yêu cầu tương tác của người dùng là 2 luồng khác nhau.

2. Các trạng thái của Thread trong Java như thế nào?

Với Thread thì những luồng này hầu hết được quản lý bởi máy ảo JVM ở trong Java. Do vậy, các trạng thái của luồng sẽ được Java định nghĩa theo các thuộc tính Static ở các lớp thread.

Dưới đây sẽ à các trạng thái cơ bản của Thread trong Java hiện nay:

- Trạng thái New: Đây là trạng thái mới của luồng tức là luồng vừa mới được khởi tạo và chưa được đưa vào khởi động và thực hiện các nhiệm vụ của mình. Đây là trạng thái mà luồn chưa được cung cấp các tài nguyên cần thiết, do đó cũng chưa thể chạy các chương trình cụ thể. 

- Trạng thái Runnable: Sau khi trải qua trạng thái New thì các luồng sẽ được chuyển sang trạng thái Runnable. Ở trạng thái này thì các luồng test đã được cung cấp các tài nguyên cần thiết cũng như các nhiệm vụ, lịch điều phối từ CPU cho các luồng test này đã bắt đầu có hiệu lực hoạt động. Thực tế thì luồng ở đây sẽ không phải luôn luôn chạy mà thay vào đó là việc điều chỉnh, phụ thuộc vào hệ thống cũng như sự điều phối khác nhau từ CPU.

- Trạng thái Waiting: Đây là trạng thái chờ của luồng. Các luồng có thể chờ một cách không giới hạn tùy thuộc vào việc có một luồng khác đánh thức nó hay không. Do vậy, nếu như không có yêu cầu hay có sự tương tác nào thì luồng sẽ luôn luôn ở trạng thái chờ.

- Trạng thái Timed-waiting: Đây là trạng thái chờ có giới hạn về mặt thời gian của luồng. Tức là luồng sẽ chỉ chờ trong một khoảng thời gian cụ thể nhất định mà thôi. Hoặc là trong trường hợp có một luồng khác đã đánh thức nó.

- Trạng thái Blocked: Đây được coi như một trạng thái “Not Runnable” của luồng. Ở trạng thái này thì luồng vẫn còn sống, tuy nhiên nó lại không được lựa chọn để chạy và thực hiện các nhiệm vụ. Thay vào đó, thread sẽ ở chế độ chờ cho đến khi một monitor xuất hiện để nó sử dụng vào mục đích unblock một đối tượng nào đó mà nó cần.

- Trạng thái Terminated: Thread sẽ có thể ở trạng thái Terminated hay ở trạng thái dead nếu như phương thức chạy của nó bị thoát ra khỏi hệ thống. 

Đây là các trạng thái hay các vòng đời của thread mà nó sẽ trải qua trong Java. Việc hiểu rõ vòng đời cũng như trạng thái có thể xuất hiện của luồng sẽ giúp bạn có thể làm việc với hệ thống tốt hơn.

3. Những cách tạo luồng ở trong Java

Tạo luồng ở trong Java hiện có thể được tạo bằng 2 cách. Đó là tạo luồng thông qua extend từ lớp Thread hoặc bằng cách implements từ việc interface Runnable. 

3.1. Cách tạo luồng thông qua Extend từ class Thread

Với cách tạo luồng này, để tạo được luồng chuẩn bạn có thể thực hiện theo hướng dẫn sau:

- Thực hiện việc khai báo 1 lớp mới được tạo ra, thừa hưởng từ lớp Thread

- Ghi đè lại phương thức đã sử dụng để chạy ở lớp này, khi luồng bắt đầu chạy các công thức, tính năng trong phương thức đó sẽ được sử dụng, thực thi một cách tự động. Tất cả những câu lệnh được sử dụng trong phương thức đó cũng sẽ tự hủy ngay khi luồng đã chạy xong.

- Thực hiện việc lập ra một đối tượng cho lớp được tạo mà ta vừa khai báo.

- Sử dụng phương thức Start để bắt đầu triển khai thực thi luồng này của đối tượng mà ta vừa tạo ra đó.

Tuy nhiên, trong cách này có một vài điều mà trong quá trình tạo luồng chúng ta cần lưu ý:

- Với việc khai báo 1 lớp mới những câu lệnh trong phương thức run thì để thực thi luồng ta sẽ phải gọi phương thức start, sau đó, khi được cung cấp đầy đủ những tài nguyên cần thiết ta mới đến để thực hiện phương thức run. Đây là một điều đặc biệt trong lớp Thread được Java xây dựng nên. Nếu như gọi phương thức run nhưng không gọi Start thì điều này sẽ giống với việc gọi 1 phương thức bình thường của một đối tượng bình thường và những câu lệnh trong phương thức đó sẽ chạy trên luồng đã gọi nó chứ không phải là chạy trên một luồng mới được tạo ra. Như vậy, vẫn sẽ chỉ có 1 luồng là làm việc chính, vì thế ứng dụng không được gọi là Multi-thread.

- Một Thread đã được Start thì sẽ không gọi lại phương thức Start được nữa. nếu như bạn quyết định làm như vậy thì sẽ có những trường hợp ngoại lệ xảy ra.

3.2. Cách tạo luồng thông qua Implement từ Interface Runnable

Đây là cách thứ 2 các bạn có thể sử dụng để thực hiện việc tạo luồng cho mình. Để thực hiện theo cách này thì dưới đây sẽ là hướng dẫn cụ thể dành cho bạn. Các bạn có thể làm theo các bước sau đây:

- Thực hiện việc khai báo tạo ra 1 lớp mới được kế thừa từ Interface Runnable.

- Ghi lại các câu lệnh trong phương thức run ở ngay tại lớp này. Khi luồng bắt đầu hoạt động thì những câu lệnh này sẽ được thực hiện. Sau khi các câu lệnh của phương thức run sử dụng trước đó được chạy xong thì luồng cũng sẽ tự hủy.

- Thực hiện việc tạo ra một đối tượng mới của lớp mà ta vừa khai báo bên trên. 

- Thực hiện việc tạo 1 đối tượng mới cho lớp thread thông qua phương thức khởi tạo là thread (runnable target - 1 lớp được thừa hưởng từ Runnable).

- Thực hiện việc gọi Start cho đối tượng mới của lớp thread vừa được tạo ra ở bước trên.

Việc sử dụng cách này giúp bạn có thể sử dụng để giải quyết được vấn đề. Với cách này, bạn sẽ không phải thực hiện việc tạo 1 lớp kế thừa của lớp Thread ban đầu. nếu như trường hợp các thiết kế bắt buộc yêu cầu phải sử dụng đa kế thừa thì lúc này, chỉ có Interface mới có khả năng giải quyết vấn đề mà thôi. 

Với những trường hợp còn lại thì ta có thể sử dụng việc kế thừa từ lớp Thread ban đầu. 

4. Ưu và nhược điểm của Multi-thread là gì?

Đa luồng là cách hiện đang được các lập trình viên sử dụng rất phổ biến. Vậy, việc sử dụng hình thức này có ưu và nhược điểm như thế nào?

4.1. Những ưu điểm của đa luồng

Những ưu điểm của Multi-thread có thể nhắc đến như:

- Đa luồng sẽ không chặn các yêu cầu, tương tác mà người sử dụng muốn thực hiện. Bởi tính chất của đa luồng là các luồng làm việc độc lập, nó cho phép bạn có thể thực hiện nhiều công việc cùng một lúc.

- Các luồng trong quá trình chạy có thể sử dụng chung và thực hiện việc chia sẻ nguồn tài nguyên với nhau. Thế nhưng về quá trình làm việc cũng như chức năng thì vẫn là độc lập.

- Trong trường hợp có 1 luồng xảy ra các ngoại lệ thì các luồng khác đều không bị ảnh hưởng. Điều này là do tính độc lập của các luồng.

- Đa luồng giúp tiết kiệm thời gian do có thể thực hiện được nhiều hành động cùng một lúc. Điều này giống như việc luồng chính dùng để thực hiện các giao diện chính với người dùng còn luồng phụ thì sẽ xử lý các kết quả tương tác bên ngoài để gửi tới luồng chính.

4.2. Những hạn chế của đa luồng

Bên cạnh việc đem lại rất nhiều tiện ích cho cả người lập trình và người sử dụng. Thế nhưng Multi-thread vẫn còn tồn tại một số hạn chế nhất định. Vậy, những hạn chế đó là gì?

- Việc có quá nhiều luồng vừa là ưu điểm cũng là nhược điểm của Multi-thread. Thực hiện được nhiều chức năng thế nhưng, với việc có quá nhiều luồng thì điều này sẽ dẫn đến khó khăn trong việc xử lý các vấn đề liên quan. Do đó, cần sử dụng một số luồng cụ thể, vừa phải, phù hợp với từng ứng dụng khác nhau.

- Đa luồng dẫn đến việc bộ nhớ của hệ thống sẽ phải thực hiện việc lưu trữ nhiều hơn, việc đồng bộ hóa cũng trở nên khó khăn hơn do có quá nhiều nguồn thông tin cần phải xử lý và lưu trữ. Vì vậy, đây được coi là một trong những hạn chế khó xử lý nhất của Multi-thread.

- Đôi khi việc chứa đựng quá nhiều luồng lại khiến người lập trình không thể kiểm soát và nắm bắt được tất cả những luồng có trong ứng dụng. Thực tế, sẽ có những luồng chết, luồng không chạy và không làm bất kỳ nhiệm vụ gì ở trong ứng dụng. Vì vậy, cần phải kiểm tra và phát hiện ra những luồng này để có thể loại bỏ tránh việc gây ra các lỗi ở ứng dụng cũng như việc quá tải bộ nhớ do lưu trữ quá nhiều luồng, kể cả những luồng không có chức năng cụ thể gì.

Nhìn chung, Multi-thread có chức năng rất hữu ích cho người dùng cũng như lập trình viên hiện nay. Tuy vẫn còn những nhược điểm nhưng không thể phủ nhận các ưu điểm của tiến trình này. Trên đây là những thông tin khá chi tiết và cụ thể về Multi-thread mà Phương Anh muốn gửi tới các bạn. Mong rằng, qua bài viết này các bạn đã hiểu rõ hơn về Multi-thread cũng như cách tạo ra các luồng trong Java để có thể ứng dụng một cách tốt nhất vào trong công việc của mình.

Đăng ngày 20/11/2020, 46 lượt xem