Khái niệm Thread là gì

Được chỉnh sửa ngày 03/06/2021.

Chào mừng các bạn đã đến với bài học Java số 41, bài học về Thread.Đây là bài học trongchuỗi bài về lập trình ngôn ngữ Javacủa Yellow Code Books.

Thời gian qua có rất nhiều bạn hỏi mình kiến thức Java bao giờ sẽ kết thúc. Vâng, mình cũng muốn thông báo với các bạn rằng chúng ta cũng đang cận kề với các bài học cuối cùng lắm rồi. Tuy nhiên mình cũng muốn các bạn biết rằng, thực ra cái sự kết thúc này chỉ là ở chừng mực các bài học của mình mà thôi. Kiến thức Java còn rất nhiều, mình hi vọng sẽ còn được nói nhiều hơn về các khía cạnh khác của Java ở các chủ đề khác. Và trong khi các bài viết sẽ không thể theo kịp tốc độ ham học hỏi của các bạn, thì mình mong các bạn cũng nên thủ sẵn cho riêng bạn các kho tài liệu học tập Java giá trị khác nhé.

Quay lại bài học về Thread. Như mình có nói từ bài học số 1, ngay khi bạn vừa mới chập chững biết đến Java là gì, thì mình cũng có nói đến một trong những thế mạnh của ngôn ngữ này, đó là Hỗ trợ chạy đa nhiệm (Multithread) rất tốt. Và mãi cho đến bài học hôm nay, thì chúng ta mới cùng nhau làm rõ hơn cái sự mạnh mẽ này của Java.

Thread hay Multithread đều có ý nghĩa như nhau trong kiến thức của bài học này. Thread dịch ra tiếng Việt là Luồng, và Multithread là Đa luồng. Luồng ở đây chính là Luồng xử lý của hệ thống. Và bởi vì lý do chính đáng để cho Thread ra đời cũng chính là để cho các ứng dụng có thể điều khiển nhiều Thread khác nhau một cách đồng thời, mà nhiều Thread đồng thời như vậy cũng có nghĩa là Đa Thread, hay Multithread. Chính vì vậy mà kiến thức Thread hay Multithread cũng chỉ là một.

Vai trò của Thread hay Multithread dĩ nhiên là cái gì đó liên quan đến Đa Luồng, Đa Nhiệm rồi. Nói cụ thể ra đó là hệ thống sẽ hỗ trợ chúng ta tách các tác vụ của ứng dụng ra làm nhiều Luồng (hay Thread), và hệ thống sẽ giúp xử lý các Luồng này một cách đồng thời. Như vậy nếu theo như những gì chúng ta làm quen với Java từ trước đến giờ, đó là nếu chúng ta có các tác vụ A, B, C, với các cách code cũ, hệ thống sẽ luôn xử lý tuần tự các tác vụ này, giả sử A sẽ được xử lý trước tiên, rồi đến B, và cuối cùng là đến C. Nhưng sau bài học hôm nay, nếu chúng ta tổ chức sao cho mỗi A, BC là mỗi Thread, thì sẽ rất tuyệt vì chúng ta hoàn toàn có thể kêu hệ thống xử lý cả A, B và C cùng một lúc.

Một ví dụ thực tế để dễ hình dung hơn về Thread như sau. Giả sử bạn đang dùng ứng dụng Google Maps trên nền Android được viết bằng Java. Bạn có nhu cầu tìm một địa chỉ trên bản đồ, địa chỉ đó làChợ Bến Thành chẳng hạn.

Ví dụ về việc sử dụng Thread

Khi bạn gõ từng chữ cái vào khung tìm kiếm ở trên cùng của màn hình, bạn mong muốn được thấy các gợi ý địa điểm sẽ liên tục được cập nhật theo từng chữ bạn gõ vào ở dưới. Như hình trên. Bạn thấy rằng, nếu không có Thread, hệ thống sẽ đợi bạn gõ xong một chữ, rồi mới bắt đầu tìm kiếm và gợi ý các địa điểm liên quan đến chữ đó, và trong lúc hệ thống đang tìm kiếm, bạn không thể gõ được chữ kế tiếp, vì với cách làm này, cùng một lúc hệ thống chỉ đáp ứng một chuyện thôi, hoặc là nhận ký tự bạn gõ hoặc là tìm kiếm, hết. Nhưng với việc áp dụng Multithread, chúng ta sẽ có 2 Thread chạy song song, một Thread nhận dữ liệu nhập vào của người dùng, Thread còn lại cứ dựa vào dữ liệu đã nhập đấy mà tìm kiếm, điều này làm cho trải nghiệm của người dùng được trọn vẹn, hệ thống mượt mà, nhanh chóng, không bị giật.

Bạn có thể xem mình minh họa việc đáp ứng của hệ thống đối với từng trường hợp tìm kiếm như ví dụ trên ở hình bên dưới. Trong đó, để tìm thấy kết quả Chợ Bến Thành xuất hiện trong danh sách gợi ý. Thì với cột Không Multithread bên trái, việc bạn gõ một từ rồi đợi hệ thống tìm kiếm, thì kết quả tìm được sẽ rất lâu so với cột MultiThread bên phải, việc bạn gõ và việc hệ thống tìm kiếm được tách ra, và thực hiện một cách song song nhau, là rất nhanh chóng và dễ chịu.

So sánh giữa việc sử dụng Multithread và không sử dụng Multithread

Mục này mình nói thêm, cho các bạn mới tiếp cận với Thread (thậm chí với các bạn đã hiểu rõ Thread rồi) đỡ bị lăn tăn khi đọc các tài liệu liên quan đến kiến thức hôm nay. Khi đó hẳn các bạn sẽ bị loạn lên với các khái niệm sau: Thread, Multithread, Task, Multitask, Process, Multiprocess.

Đầu tiên, ở mức bao quát nhất, bạn đều biết các hệ thống máy tính ở thời đại ngày nay đều được nhắc đến với việc có khả năng xử lý đa nhiệm. Đa nhiệm ở đây chính là việc xử lý nhiều nhiệm vụ cùng một lúc. Việc xử lý đa nhiệm này càng ngày càng phổ biến và được quan tâm nhiều hơn khi các dòng máy tính đều cho ra đời các cỗ máy với nhiều CPU. Như vậy cái nhiệm vụ mà hệ thống cần xử lý đó thường được gọi là Task, và một hệ thống đa nhiệm được gọi là Multitask.

Từ nhu cầu muốn xử lý Multitask đó, hệ thống mới định ngĩa ra các Process. Process được hiểu là một chương trình. Khi ứng dụng của bạn được khởi chạy, chúng sẽ được hệ thống tạo ra một Process và ứng dụng đó sẽ được thực thi và được quản lý bên trong Process đó. Hệ thống sẽ quản lý một lúc nhiều Process khác nhau, mỗi Process như vậy được cấp phát các tài nguyên hệ thống một cách độc lập với nhau. Và bởi cùng một lúc có thể có nhiều Process (hay nhiều ứng dụng) chạy song song với nhau, nên người ta gọi cái sự Đa Process này là Multiprocess.

Khi một Process được tạo, ứng dụng bên trong Process đó có thể tạo ra nhiều Thread khác. Về cơ bản thì Thread cũng sẽ được hệ thống quản lý như Process vậy, tức là chúng có thể được chạy song song với nhau, nên mới có thêm khái niệm Multithread. Nhưng các Thread bên trong một Process chỉ được hoạt động bên trong giới hạn của Process đó thôi. Các Thread sẽ được sử dụng các tài nguyên y như là Process của nó được phép. Nhưng có một khác biệt đó là các Thread rất nhẹ và có thể dễ dàng chia sẻ tài nguyên dùng chung với nhau bên trong một Process.

Như vậy thôi. Và mặc dù lan man nhiều khái niệm như vậy, nhưng bạn nên nhớ, bài học này chúng ta chỉ quan tâm đến một loại khái niệm mà thôi, đó chính là Thread.

Câu hỏi này tuy dễ trả lời, song nếu suy nghĩ kỹ nó lại chứa đựng nhiều thông tin hơn chúng ta tưởng.

Đầu tiên như mình có nói đến một cách giông dài trên kia rằng, nếu như bạn muốn có nhiều tác vụ muốn làm việc đồng thời với nhau, thì hãy sử dụng các Thread. Chẳng hạn bạn vừa muốn ứng dụng lấy thông tin người dùng nhập gì vào khung hội thoại, vừa gọi lên server để kiểm tra kết quả nhập vào. Hay nếu bạn lập trình game, bạn vừa muốn game nhận tín hiệu điều khiển từ bàn phím, song song với việc vẽ nhân vật trên màn hình theo sự điều khiển đó, song song với việc điều khiển các chướng ngại vật, hoặc điều khiển các đường đạn bắn, các hiển thị về điểm số, v.v

Ý thứ hai trong việc sử dụng Thread là,cũng na ná như việc muốn xử lý các tác vụ đồng thời, nhưng khi này bạn mong muốn được điều khiển các Luồng theo một sự đồng bộ nhất định. Chẳng hạn bạn khởi động nhiều Thread trong một ứng dụng, nhưng bạn muốn các Thread khác tuy được khởi động nhưng khoan hãy chạy, mà phải đợi một Thread nào đó kết thúc thì mới được hoạt động. Ở Tập 3 của loạt bài về Thread mình sẽ nói rõ cách sử dụng này của Thread.

Ý thứ ba trong việc sử dụng Thread, cũng không ngoài việc muốn xử lý các tác vụ đồng thời, nhưng khi này là tình huống khi bạn muốn ứng dụng thực thi các tác vụ quá lớn. Lớn ở đây thường là lớn về mặt thời gian. Ví dụ khi ứng dụng phải download một file từ server, hay phải thực hiện công việc gì đó mà thời gian hoàn thành của nó không phải tức thời. Khi đó nếu không có Thread, các tác vụ lâu lắc này có thể sẽ làm ảnh hưởng nghiêm trọng đến trải nghiệm của người dùng.

Chúng ta sẽ đến phần ví dụ về Thread thông qua bài thực hành sau đây để giúp bạn hiểu rõ hơn.

Bài thực hành này giúp bạn có một cảm quan ban đầu về việc Threadlà gì và nó hoạt động như thế nào. Mình không mong muốn các bạn phải hiểu hết các dòng code của bài thực hành này, nhưng mình hi vọng các bạn sẽ thử code, và thực thi thành công chương trình. Nếu có bất kỳ lỗi nào phát sinh xảy ra với bạn thì cứ liên lạc với mình hoặc để lại comment ở dưới bài viết hôm nay nhé.

Bạn cứ tưởng tượng rằng bài thực hành này đang xây dựng một trò chơi. Trong trò chơi này có một bàn xoay, trên đó có đánh các con số từ0đến100. Người chơi sẽ bắt đầu xoay bàn xoay, sau khi bàn xoay được xoay thì người chơi chẳng nhìn thấy các con số trên đó, cho đến khi họ dừng bàn xoay lại, thì con số nào ở ngay người chơi chính là con số được người chơi chọn lựa.

Ồ, ứng dụng của chúng ta sẽ không xây dựng theo kiểu một trò chơi hoàn chỉnh như thế đâu. Thay vào đó chúng ta sẽ tập trung vào logic của trò chơi. Chúng ta sẽ thay việc xoay bàn xoay bằng một lệnh gõ vào ký tự bất kỳ trênconsole. Sau khi nhận ký tự vừa gõ, thay vì có một bàn xoay xoay tít, thì chúng ta sẽ khởi chạy một Thread, Threadnày sẽ lần lượtquay các con số, sao cho nó đếm tuần tự từ0đến100rồi lại quay lại số0. Threadcủa chúng ta chạy mãi cho đến khi người chơi nhập thêm một ký tự từ console và Threadkết thúc. Con số mà Threadvừadừnglại chính là số được người chơi chọn lựa.

Bạn đã hiểu yêu cầu của việc xây dựng trò chơi này rồi đúng không nào. Để bắt đầu vào xây dựng trò chơi, bạn nên tự tạo một project mới bằng Eclipse hoặc InteliJ. Bạn có thể đặt tên cho project này làThreadLearningcũng được. Sau đótạo mới một class MainClass có chứa phương thức main()trong đó.

Tiếp theo, bạn hãy tự tạo mới một lớp mới có tênCountTheNumberThread. Khoan hãy code gì cho lớp này cả. Lớp này chính là Threadvới trách nhiệmquay vòngcác con số như chúng ta đã bàn đến. Tổng quan thì ứng dụng của chúng ta có cấu trúc như hình sau.

Cấu trúc các lớp trong ứng dụng

Nào giờ bạn hãy code cho thân lớpCountTheNumberThreadnhư sau.

public class CountTheNumberThread extends Thread { private int count = 0; private boolean isStop = false; @Override public void run() { while (!isStop) { count++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (count > 100) { count = 0; } } } public void end() { isStop = true; } public int getCount() { return count; } }

Dù cho có thể bạn chưa hiểu rõ về Thread, nhưng mình cũng muốn giải thích một chút, để đến bài học sau bạn sẽ dễ dàng tiếp cận với Threadhơn.

Đầu tiên thì lớpCountTheNumberThreadmuốn thành một Threadthì nó phải kế thừa từ lớpThread(và còn có cách khác nữa để biến một lớp thành Threadmà bài sau chúng ta sẽ nói đến). Sau đó bên trong lớp này phảioverridephương thứcrun(). Chính các code bên trong phương thứcrun()sẽ là một Luồng,Luồngnày sẽ được hệ thống thực thi song song với cácLuồngđang thực thi khác nếu có trong hệ thống.

Trong CountTheNumberThread còn sử dụng phương thứcThread.sleep(100). Phương thức này làm cho các Thread đang chạy trở nênngủ trong một khoảng thời gian được tính bằng mili giây rồi thức dậy và tiếp tục chạy với vòng lặp mới. Tại sao chúng ta lại cho Thread ngủ, vì cơ bản vòng lặp while được chạy với tốc độ rất nhanh, chúng ta muốn mỗi while sẽ chậm nhịp lạ một chút (100 mili giây) để sau này thuộc tính isStop bên trong CountTheNumberThread sẽ dễ được thay đổi hơn, giảm thiểu lỗi xảy ra như ở các đoạn comment bên dưới bài học hôm nay có nói đến. Chú ý là bạn phảitry catchphương thứcThread.sleep()này với mộtChecked Exceptioncó tênInteruptedException. Và mình sẽ nói rõ về phương thứcThread.Sleep()ở bài sau nhé.

Vậy thôi, ngoài các ý lớn mình nói đến như này ra thì các khai báo còn lại củaCountTheNumberThreadđều quá đỗi quen thuộc nên mình không trình bày nhiêu khê.

Đến lúc này thìCountTheNumberThreadcũng chỉ là một lớp mà thôi. Muốn khởi chạy lớp này để trở thành mộtThreadthì mình mời bạn đến với các code ở phương thứcmain()như sau.

public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // Đợi người dùng nhấn một phím để bắt đầu System.out.println("Nhấn phím bất kỳ để quay số"); scanner.nextLine(); // Khai báo & Khởi chạy CountTheNumberThread như là một Thread thông qua // phương thức start() CountTheNumberThread countingThread = new CountTheNumberThread(); countingThread.start(); // Đợi người dùng nhấn một phím để kết thúc System.out.println("Nhấn phím bất kỳ để kết thúc quay số"); scanner.nextLine(); // Ngừng Thread và xem hiện đang "quay" tới số nào countingThread.end(); System.out.println("Con số may mắn: " + countingThread.getCount()); }

Nhìn vào code trên, bạn có thể thấy chút khác biệt giữa việc sử dụng mộtThreadso với một lớp bình thường khác. Nếu như việc khởi tạo một lớp bình thường mà bạn biết so với mộtThreadlà như nhau. Thì với mộtThread, để khởi chạyThreadđó (tức là bạn biến nó thành mộtLuồngtrong hệ thống), bạn phải gọi phương thứcstart()của nó, đây là phương thức được xây dựng sẵn ở lớpThread. Khistart()được gọi,Threadcha sẽ khởi chạy các code bên trong phương thứcrun()mà bạn đã khai báo bên trongCountTheNumberThread. Và thế là việc đón nhận dữ liệu gõ vào từ bàn phím ở phương thứcmain()sẽ song song với vòng lặp bên trong phương thứcrun()này, không code nào phải chờ đợi code nào cả.

Nếu bạn thực thi chương trình, hãy nhập một ký tự vào console để bắt đầuquay sốvà nhập một lần nữa để kết thúc, bạn sẽ nhận được một số may mắn của riêng bạn như sau.

Kết quả thực hành với Thread

Opps! Mình quay ra số69, còn bạn thì sao. Chúc bạn quay tay may mắn.

Với bài tập này mình muốn bạn hãy thử bỏ qua việc kế thừaThreadcủa lớpCountTheNumberThread, khi đó thì phương thứcrun()của lớp này cũng không còn từ khóaoverridenữa, như vậy bạn đã biến lớp này thành một lớp bình thường vàrun()cũng chỉ là một phương thức bình thường khác. Rồi sau đó ởmain()bạn đừng gọicountingThread.start() nữa mà hãy gọicountingThread.run(). Tức là bạn không sử dụngThreadtrong trường hợp này. Bạn hãy thực thi lại ứng dụng và so sánh xem việc áp dụngThreadvới trò chơi này sẽ khác với việc không áp dụngThreadsẽ như thế nào nhé.

Chúng ta vừa mới tiếp cận kiến thức khá mới mẻ và thú vị của Java về vấn đề Đa nhiệm, cụ thể là Thread. Bài hôm nay chỉ mới là các kiến thức làm quen ban đầu, Thread còn rất nhiều kiến thức thú vị khác mà mình sẽ lần lượt trình bày ở các bài viết sắp tới nữa.

Cảm ơn bạn đã đọc các bài viết củaYellow Code Books.Bạn hãy ủng hộ blog bằng cách:
Đánh giá 5 saoở mỗi bài viết nếu thấy thích.
Commentbên dưới mỗi bài viết nếu có thắc mắc.
Để lại địa chỉ emailcủa bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
Chia sẻ các bài viếtcủa Yellow Code Books đến nhiều người khác.
Ủng hộ blogtheo hướng dẫn ở thanh bên phải để blog ngày càng phát triển hơn.

Bài Kế Tiếp

Sau bài làm quen hôm nay, chúng ta sẽ đến phần chính của Thread. Đó chính là các kiến thức về các cách khởi tạo một Thread. Và vì Thread được xem như một tác vụ độc lập, nên chúng ta cũng sẽ xem việc kết nối với nhau giữa các Thread như thế nào nhé.

Video liên quan

Post a Comment (0)
Previous Post Next Post