Giải pháp chuyển đổi số trọn gói cho doanh nghiệp Việt
Sau khi tập 1 về Thread ra lò, mình nhận được nhiều chia sẻ và phản hồi từ các bạn. Mình cảm nhận được mối quan tâm rất lớn của các bạn với kiến thức này. Điều này thật sự thú vị. Thực ra mình cũng từng rất thích thú khi tiếp cận với Thread. Tuy chỉ là một kiến thức nhỏ nhoi trong biển kiến thức Java, nhưng Thread như mang đến một làn gió mới, một khả năng mới để chúng ta xây dựng các ứng dụng đa nhiệm, mạnh mẽ, thiết thực hơn, tận dụng tối đa hiệu năng của hệ thống hơn. Và đặc biệt hơn nữa, sau khi biết đến Thread là gì, thì chúng ta đã có thể bắt tay vào tìm hiểu các kiến thức về xây dựng một game viết bằng Java được rồi đấy.
Vậy hôm nay, chúng ta sẽ tiếp tục củng cố cái sự quan tâm đối với Thread bằng cách đi cụ thể hơn về nó, chúng ta sẽ nói đến các cách thức để khai báo và khởi tạo một Thread.
Ở bài hôm trước bạn cũng đã làm quen với một cách để tạo ra một Thread rồi. Nhưng mình mong muốn bài hôm nay bạn hãy… quên kiến thức bài trước đi, chúng ta cùng đi lại từ đầu cho nó hệ thống nào.
Trong Java, có hai cách để bạn tạo một Thread. Tuy cả hai cách đều giúp bạn tạo ra một Thread, nhưng mỗi cách lại có những mục đích và lợi ích khác nhau. Nhiệm vụ của bạn là phải biết rõ cả hai cách này. Tại sao phải biết cả hai cách? Ngoài việc bạn phải biết hết để có thể khai báo và sử dụng, bạn còn phải biết để còn đọc hiểu source code của người khác khi họ không dùng giống bạn nữa.
Vậy hai cách để tạo ra Thread là gì.
Cách này ở bài hôm trước… ồ mình đã kêu các bạn quên đi rồi mà. Vậy thì, với cách này bạn làm như sau.
Thật đơn giản đúng không nào. Không ngờ kiến thức về Thread lại dễ đến vậy. Như bạn đã biết sơ qua từ bài hôm trước rằng, để khai báo một lớp là Thread, thì đơn giản chỉ kế thừa nó từ lớp cha Thread, chính phương thức run() bên trong lớp đó sẽ trở thành một Luồng xử lý bởi hệ thống khi đâu đó bên ngoài gọi đến phương thức start() của lớp này.
Chúng ta cùng đến với bài thực hành để hiểu rõ hơn.
Bài thực hành này chúng ta thử nghiệm tạo một Thread đếm ngược 10 giây. Khi start, Thread sẽ bắt đầu in ra console giá trị 10, mỗi một giây trôi qua Thread sẽ giảm con số này đi một đơn vị và lại in ra console, đến khi giảm đến giá trị 0 Thread sẽ in “Hết giờ”.
Bạn có thể sử dụng lại project đã tạo từ bài hôm trước, hôm nay bạn tạo một lớp mới có tên CountDownThread. Lớp này sẽ kế thừa từ lớp Thread như những gì mình đã nói ở các gạch đầu dòng trên kia như sau.
Một khung sườn cho Thread chỉ như vậy thôi. Như đã nói, CountDownThread khi được start sẽ bắt đầu đếm ngược từ 10 giây, đến 0 giây sẽ hiển thị chuỗi “Hết giờ”. Việc hiển thị số giây ra console thì bạn biết rồi, mình chỉ bật mí là để làm cho con số này chỉ được cập nhật và hiển thị ở mỗi giây thì chúng ta sử dụng phương thức Thread.sleep(1000). Phương thức này bạn đã làm quen từ bài hôm trước rồi. Mình nhắc lại một tí, nó sẽ giúp làm cho các Thread đang chạy trở nên “ngủ” trong một khoảng thời gian được tính bằng mili giây, trong trường hợp này chúng ta truyền vào 1000 mili giây, tức là 1 giây. Sau khi ngủ hết thời lượng cho phép, Thread sẽ “thức dậy” và thực hiện tiếp tác vụ của nó. Chú ý là bạn phải try catch phương thức Thread.sleep() này với một Checked Exception có tên InteruptedException. Và mình sẽ nói rõ về phương thức Thread.Sleep() ở bài sau nhé. Còn đây là code hoàn chỉnh của CountDownThread.
Để khởi chạy Thread vừa tạo thì chúng ta sẽ gọi phương thức start() của nó như sau.
Còn đây là màn hình console của “bộ đếm giờ” mà chúng ta vừa tạo. Cứ mỗi một giây sẽ có một con số xuất hiện cho đến khi chữ “Hết giờ” xuất hiện cuối cùng sẽ là lúc kết thúc chương trình (và kết thúc cả CountDownThread).
Nếu như cách trên kia thì bạn phải kế thừa từ lớp Thread, thì cách này bạn lại implement một interface có tên Runnable. Với cách này bạn làm như sau.
Nghe có vẻ phức tạp hơn cách thứ nhất trên kia đúng không nào. Nhưng bạn cũng nên thử qua cho biết bằng cách đến với bài thực hành sau.
Chúng ta vẫn sẽ xây dựng lại ví dụ về một Thread đếm ngược 10 giây trên kia bằng cách thứ 2 này.
Với cách này thì bạn chỉ cần chỉnh sửa một tí ở lớp CountDownThread của bạn, sao cho từ extends Thread sang implements Runnable là xong. Bạn hãy xem code sau sẽ rõ.
Vấn đề khai báo một Thread không khác nhau lắm giữa hai cách đúng không nào. Khác biệt nhiều hơn sẽ nằm ở cách khởi chạy Thread. Code ở phương thức main() sẽ phải thay đổi như sau.
Bạn hãy thực thi lại chương trình. Kết quả hai cách làm này đều cho ra kết quả như nhau cả.
Nếu bạn đã quên lớp Vô Danh là lớp gì rồi, thì có thể đọc lại bài học ở link này.
Còn nếu bạn thắc mắc Thread thì liên quan gì đến lớp Vô Danh? Thì mình sẽ giải thích sơ qua thế này. Cơ bản thì Thread được xem là một cách gọn nhẹ cho hệ thống (và cả chúng ta) để thực thi các tác vụ song song. Và để làm cho sự gọn nhẹ đó càng thêm gọn nhẹ (về mặt quản lý code), thì việc kết hợp giữa Thread và lớp Vô Danh sẽ là giải pháp tốt cho ý này. Vì khi đó, chúng ta sẽ không cần thiết phải khai báo rõ ràng một lớp Thread nào cả, chỉ đơn giản là dựng lên một lớp Vô Danh, và start nó thôi.
Việc kết hợp giữa Thread và lớp Vô Danh là khá phổ biến, và người ta đã gộp 2 cái tên này lại thành một tên chung, gọi là Thread Vô Danh (Anonymous Threads).
Nào chúng ta cùng xem các cách sau để “vô danh hóa” một Thread. Mình dùng lại ví dụ Thread đếm ngược trên kia để bạn xem nhé.
Nào, chúng ta cùng tạo lại một Thread từ việc kế thừa lớp Thread, nhưng “vô danh hóa” nó như sau.
Đấy, trên đây là một Thread Vô Danh. Đến đây sẽ có nhiều bạn thắc mắc rằng Thread Vô Danh thực chất có giúp làm gọn hơn cho việc quản lý code hay không. Thì mình có vài ý muốn trao đổi thêm như sau.
Thực ra việc sử dụng Thread Vô Danh so với Thread bình thường có làm code trở nên gọn hay không cũng tùy vào cách nhìn code của mỗi người thôi. Bạn xem, với bài thực hành xây dựng một Thread bình thường trên kia (mình sẽ gọi tắt Thread-bình-thường là Thread), bạn phải xây dựng một lớp CountDownThread.java hẳn hoi, code này có cái hay là rất tường minh. Còn với code ví dụ ở mục này, chúng ta đã tạo ra một đối tượng countDownThread không phải từ lớp CountDownThread hay từ lớp Thread, mà là từ một lớp Vô Danh kế thừa từ lớp Thread nhé. Cách khai báo này giúp giảm đi việc phải tạo ra một file Java nào khác, chúng ta chỉ đơn giản khai báo và dùng thôi, ngoài ra thì Thread Vô Danh còn dùng được các thành viên của lớp chứa nó nữa.
Điểm khác biệt nữa giữa việc khai báo một Thread và một Thread Vô Danh là, với Thread bạn có thể xây dựng constructor cho nó, nên bạn có thể truyền vào Thread các biến nào đó phục vụ cho logic của ứng dụng. Còn Thread Vô Danh thì không có constructor, nên bạn có thể phải dùng biến toàn cục của lớp khai báo.
Nhưng bạn cũng nên cân nhắc, dù cho cách sử dụng Thread Vô Danh khá là nhanh chóng và tiện lợi, chúng có thể sẽ làm code ở lớp sử dụng này phình lên, khó quản lý hơn nếu có quá nhiều Thread Vô Danh như thế này đấy nhé.
Quay lại kiến thức của Thread Vô Danh, với code trên đây, chúng ta còn có thể viết gọn hơn lại nữa cơ. Bằng việc không cần phải khai báo tên đối tượng, mà có thể start() luôn, như thế này.
Nếu bạn hiểu Thread Vô Danh từ cách kế thừa lớp Thread trên kia, thì việc tạo một Thread Vô Danh từ interface Runnable có lẽ bạn cũng có thể tự viết được.
Code này cũng có thể viết ngắn gọn hơn bằng cách bỏ đi khai báo đối tượng từ lớp Thread như sau.
Hoặc có thể ngắn gọn hơn nữa khi không cần khai báo đối tượng của Thread Vô Danh. Nhưng khi này bạn phải truyền lớp Vô Danh này vào Thread như là một tham số. Như sau.
Hi vọng kiến thức về Thread Vô Danh không làm bạn quá đau đầu. Mình mời các bạn cùng đến với bài tập sau đây để có thể “quen tay” hơn trong việc tạo ra một Thread.
Yêu cầu bài tập như sau. Bạn hãy tạo ra một trò chơi để người dùng có thể nhập vào một số nguyên trong khoảng từ 1 đến 100. Sau đó bạn xây dựng một Thread đoán số, Thread này sẽ đạo ra các con số random cũng trong khoảng 1 đến 100 đó. Cứ mỗi lần random được một số, Thread sẽ in ra console cho người chơi có thể nhìn thấy. Thread sẽ dừng lại khi random ra một số trùng với số mà người chơi vừa nhập, đồng thời in ra số lần “đoán” để ra được con số đó.
Lưu ý rằng, có 2 Thread cùng đoán số, để “thi thố” xem Thread nào “đoán” ra con số của người chơi nhanh nhất.
Để dễ hình dung hơn, mình đưa ra kết quả console dự kiến của trò chơi sẽ như thế này. Kết quả này dựa trên sự “đoán” cật lực của 2 Thread, để có thể biết được người chơi đã nhập vào con số 15. Và như hình thì Thread 2 đã thắng với 68 lần đoán. Thread 1 “kém thông minh” hơn và tiếp tục đoán cho đến lần đoán thứ 360.
Mình có hai gợi ý để bạn chỉ tập trung vào code cho Thread thôi, đỡ phải lăn tăn tìm hiểu code khác trên mạng.
Xong rồi, mời bạn code.
Sau khi code xong, bạn có thể so sánh với đáp án của mình. Đầu tiên là Thread đoán số, mình đặt tên nó là GuessANumberThread.
Còn đây là nơi kêu người dùng nhập vào một con số cần đoán, và tạo ra 2 Thread để thi thố. Nơi này chính là phương thức main().
Kết quả thực thi chương trình sẽ như hình trên kia.
Bạn hãy thử code lại trò chơi đoán số trên đây bằng Thread implement từ Runnable nhé.
Chỉ có một lưu ý cho bạn dễ code rằng, để có thể gọi đến tên của Thread implement từ Runnable, thì không thể cứ gọi getName() như Bài tập 1 được, mà bạn phải gọi Thread.currentThread().getName().
Lần này bạn hãy code lại trò chơi đoán số này bằng Thread Vô Danh xem sao nhé.
Đây là cách mình làm, cách của các bạn thế nào.
Trên đây là tất cả các cách để bạn có thể tạo ra một Thread. Các bạn thấy thế nào, Thread có dễ dùng hay không. Hãy để lại comment bên dưới bài học nếu bạn có bất cứ thắc mắc nào nhé.
Chưa có câu hỏi nào cho bài này.