Toggle Theme Editor
Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Charcoal

[Tutorial] Sử dụng Thread trong java

Discussion in 'Hướng dẫn người mới bắt đầu' started by kim, 26/8/12.

  1. kim

    kim Thành viên VIP Staff Member

    Trong lập trình Thread được sử dụng rất phổ biến, đặc biệt là trong những ứng dụng game thì điều này hoàn toàn chắc chắn. Nhưng đối với những người mới học thì định nghĩa này còn khá mơ hồ đúng không nào? Vậy Thread là gì nhỉ? Bài này chúng ta sẽ cùng tìm hiểu và trao đổi những vấn đề liên quan đến Thread. Mình sẽ cập nhật bài viết từ từ, mong sớm nhận được những phản hồi tích cực hoặc những đóng góp của bạn để bài viết sớm hoàn thành.

    Định nghĩa: Thread là một tiến trình tách ứng dụng ra một hoặc nhiều luồng khác nhau để cùng thực hiện nhiều nhiệm vụ cùng một lúc. (Tự định nghĩa)
    [​IMG]
    Có thể tạm hiểu, tôi nhấn mạnh từ "tạm hiểu"nhé! Chiếc xe tăng và người đàn ông là hai luồng khác nhau cùng thực hiện đồng thời các hành động khác nhau (thực tế game sẽ không cần làm như thế). Như vậy các bạn chỉ quan tâm đến từ đồng thời khi nói đến Thread là đủ!

    Thread được tạo bời lớp java.lang.Thread
    Khai báo và sử dụng
    PHP:
    public class DemoThread {
        public static 
    void main(String[] args) {
            
    Thread th1 = new Thread(); //Khởi tạo Thread

            
    th1.start(); //Thực thi Thread th1
        
    }
    }
    Đoạn code trên là cơ bản về tạo và thực thi một Thread.
    Tiếp theo chúng ta sẽ định nghĩa nội dung mà Thread được thực hiện như thế nào bằng cách viết code trong phương thức run() (mặc định như đoạn code phía trên thì chương trình sẽ thực thi phương thức run() với nội dung rỗng, đồng nghĩa với việc không làm gì cả).
    PHP:
    public class DemoThread {
        public static 
    void main(String[] args) {
            
    Thread th1 = new Thread(){
                public 
    void run(){
                    
    System.out.println("Thread vừa được thực thi");
                }
            }; 
    //Khởi tạo Thread

            
    th1.start(); //Thực thi Thread th1

        
    }
    }
    [​IMG]

    Người dùng tự định nghĩa Thread
    Đến đây chúng ta sẽ tìm hiểu cách để tạo ra một Thread như thế nào? Java cung cấp cho chúng ta 2 cách để tự định nghĩa một Thread đó là extend từ lớp Thread hoặc implement từ Runnable interface.

    [​IMG]
    Hình trên chúng ta thấy rằng khi Thread được implement từ Runnale thì phải chạy bằng phương thức run() do phương thức start() không có sẵn trong Runnable interface.
    Tiếp đến chúng ta sẽ định nghĩa nội dung cho hàm run() của hai Thread phía trên.

    PHP:
    public static void main(String[] args) {
            
    My_Thread my = new My_Thread();
            
    MyThread_Impl imp = new MyThread_Impl();

            
    my.start();
            
    imp.run();
            
    //Thêm dòng này nữa để kiểm tra thử
            
    System.out.println("Kết thúc chương trình!");
        }
    [​IMG]

    Ở trên tôi sử dụng thêm phương thức sleep() với giá trị 1000 milliseconds (tương đương với một giây), mục đích của việc này để tạm dừng tiến trình sau 1 giây. Lúc này kết quả sẽ được như sau:
    [​IMG]

    Như hình trên các bạn thấy cả hai tiến trình đều được thực hiện song song, vì vậy nó trộn lẫn vào với nhau, tuy nhiên các bạn để ý nếu đúng như định nghĩa thì hai tiến trình này tách biệt so với ứng dụng mới đúng và vòng lặp với mỗi lần một giây tổng cộng là 3 giây. Với khoảng thời gian này thì chương trình dư sức thực hiện in ra dòng "Kết thúc chương trình". Thế nhưng tại sao dòng "Kết thúc chương trình!" lại được thực hiện cuối cùng? Quay lại hàm main các bạn sẽ thấy.

    [​IMG]

    Chúng ta chưa kết luận vội thử thay đổi cách viết để ép Thread được thực hiện bằng phương thức start() thử xem:

    [​IMG]

    Ở trên tôi tạo một đối tượng Thread với tên "th" với tham số truyền vào là một đối tượng Thread được implement từ Runnable interface. Mục đích để chạy phương thức start() được cung cấp sẵn trong lớp java.lang.Thread
    Sau khi run kết quả như sau:
    [​IMG]
    Như vậy có thể nhận thấy rằng chương trình sẽ thực hiện từ đầu đến cuối và tách ra 2 tiến trình chạy song song luôn không ảnh hưởng gì cả, đây chính là mục đích chúng ta cần. Chương trình chạy hết hàm main còn hai tiến trình vẫn chạy đến khi kết thúc mới thôi.
    Vậy các bạn lưu ý khi sử dụng lớp được implement từ interface Runnable nhé!

    Đồng bộ hóa trong java
    Bây giờ chúng ta sẽ tìm hiểu đồng bộ hóa trong java với từ khóa synchronized

    synchronized methods
    Giả sử chồng và vợ có 2 cái thẻ ATM. Hai người cùng rút một số tiền nhất định vào cùng một thời điểm, sau khi rút tiền máy ATM hiển thị kết quả ngược lại so với mong muốn.
    [​IMG]
    Như vậy số tiền kia bay đi đâu rồi! Ai sẽ chịu trách nhiệm đây? (Chắc là ngân hàng):D
    Minh họa cho sự cố này:
    [​IMG]
    Hình trên tôi cố tình chạy hai tiến trình vào cùng một thời điểm, cùng với việc trừ 1000$ salary trong tài khoản. Tôi sử dụng thêm phương thức getName() để biết được chính xác tên của Thread hiện tại. Và được kết quả:
    [​IMG]
    Tài khoản salary của gia đình là $2000, mà vợ rút $1000 thì phải còn $1000 nhưng còn lại $0. Nguyên nhân do Thread của vợ đang rút $1000 thì Thread của chồng cũng rút $1000 vào cùng thời điểm đó dẫn đến bất đồng bộ.
    Và để giả quyết vấn đề trên ta chỉ cần thêm từ khóa synchronized vào trước kiểu giá trị trả về của phương thức là đủ. Minh họa hình dưới:
    [​IMG]

    Mục đích của chính của synchronized là khi đã có một tiến trình A sử dụng tài nguyên này rồi thì những tiến trình khác phải chờ đến khi tiến trình A kết thúc thì mới được phép sử dụng. Kết quả:
    [​IMG]
    Như vậy tiến trình của người chồng phải chờ tiến trình người vợ kết thúc mới được phép sử dụng tài nguyên, đồng nghĩa với việc thời gian xử lý sẽ tăng lên gấp đôi (do phải chờ tiến trình trước kết thúc) nhưng điều này đảm bảo rằng dữ liệu luôn luôn được toàn vẹn.

    synchronized block
    Thay vì sử dụng synchronized một phương thức như ở trên thì chúng ta cũng có thể synchronized một đoạn nhỏ trong phương thức của một đối tượng bằng cách sử dụng synchronized block, minh họa hình dưới:
    [​IMG]
    Ở hình trên tôi đã đánh dấu khối được synchronized, các bạn để ý bên cạnh từ khóa synchronized sẽ có cấu trúc "<TênClass.Class>" như trong trường hợp này sẽ là mThread.Class hoặc đơn giản hơn bạn thay thế bằng từ khóa this cũng được, mục đích của từ khóa đó dùng để chỉ rõ đối tượng đối tượng hiện tại sẽ được synchronized và tôi vừa thêm một dòng thông báo "đăng xuất [X]" để xem chuyện gì sẽ xảy ra, kết quả:
    [​IMG]
    Như vậy tiến trình người vợ sau khi thực hiện xong khối lệnh được synchronized thì sẽ nhường quyền cho tiến trình người chồng thực hiện mà không hề ép tiến trình người chồng chờ đợi thêm.
    Như vậy với cách trên chúng ta cũng có thể synchronized một đối tượng khác mà thread hiện tại đang nắm giữ với cú pháp như sau:
    PHP:
    Object obj = new Object();
      ...
    synchronized (obj) {  // in a method
      
    ... // nội dung
    }
    Đến đây xem như kết thúc phần đồng bộ hóa trong java!

    Researching and writting...
     
  2. b1u3eyes

    b1u3eyes Member

    bai viết hay, mong bạn tiếp tục phát huy :)
     
  3. Ediltz90

    Ediltz90 New Member

    :D bài viết rất tốt. Có tinh thần học hỏi và trao đổi cao.
    T chỉ bổ sung thế này. Nếu bạn thử chạy 1 vòng for 1 -> 10 và tạo ra 10 thread rồi gọi hàm run sau mỗi lần khởi tạo. Bình thường ta vẫn nghĩ rằng thằng nào tạo ra trước sẽ chạy trước, nhưng vs thread thì lại khác. Có khi cả 10 cái cùng chạy, có khi chỉ 1 vài cái và thứ tự thì k giống nhau.
    => nổi lên 1 vấn đề là synchronize :D. Chúng ta cũng nên trao đổi thêm về vấn đề này ^^
     
  4. tuanc

    tuanc New Member

    Bài viết rất bổ ích, cảm ơn bạn rất nhiều
     
  5. thethanh1207

    thethanh1207 Member

    xem xong bài này hiểu hiểu đc 1 chút rồi.thanks bạn nhé, tiếp tục nhé
     
  6. kokichi88

    kokichi88 Member

    Bài viết của bạn khá đầy đủ, nhưng cái ví dụ về game có nhiều object chạy đồng thời mà mỗi object là 1 thread thì dễ gây hiểu lầm :).
    Với 1 thread cũng có thể làm nhiều object chạy mà bạn có cảm giác là đồng thời vậy.
     
    tuanvu_n likes this.
  7. kojca

    kojca New Member

    Bạn nào có bài mẫu ngắn gọn nào về cách sử dụng thread kết hợp socket để server xử lý được nhiều Client ko. Mình làm bị lỗi có lẽ do nó chạy nhiều luồng nên có lúc nó ko nhận được luồng kia đã xử lý rồi. ai có share mình tham khảo với. Cảm ơn nhiều :D
     
  8. vtcNew

    vtcNew Active Member

    http://android4vn.com/threads/59-Gioi-thieu-lap-trinh-socket-trong-java
     
    kojca likes this.
  9. LEMINHMAN1

    LEMINHMAN1 Member

    Ví dụ rất thực tế !
     
  10. beobot

    beobot New Member

    Ở phần ghạch đỏ :

    [​IMG]

    Theo ví dụ ở phần "Người sử dụng tự định nghĩa Thread"

    [​IMG]

    thì có kết quả như sau :

    [​IMG]

    Mình muốn hỏi là khi luồng chính (main) thực hiện dòng cuối cùng của chương trình (Dòng in ra "Kết thúc chương trình") thì luồng đó kết thúc chưa hay nó đợi cho 2 luồng con thực hiện xong mới kết thúc .
     
  11. Nancru

    Nancru CongDongJava Project Leader Staff Member

    Main thread mà kết thúc đồng nghĩa với jre cũng tắt. Có định nghĩa Main Thread ngay ở trên đó.
     
  12. beobot

    beobot New Member

    Vấn đề là luồng chính (main) in ra dòng "kết thúc chương trình" là dòng cuối cùng của thread chính.
    Mà kết quả của các luồng con sau đó mới được in ra .
    Vậy thì luồng chính (main) này đã được kết thúc trước 2 luồng con kia chưa ? hay đợi 2 luồng con được tạo ra trong main thực hiện xong mới kết thúc .
     
  13. kim

    kim Thành viên VIP Staff Member

    Bài này chúng ta chỉ hướng đến cách sử dụng Thread ở mức cơ bản thôi bạn à, bản thân mình cũng không quan tâm đến việc chương trình chính kết thúc thì Thread con có kết thúc hay không? Nhưng nếu để ý một xíu... Giả sử dùng Thread để update của đồng hồ trên một Application thì việc tắt Application đó cũng đồng nghĩa với việc kết thúc Thread con đó. Như vậy theo mình nghĩ vấn đề bạn đề cập cũng sẽ tương tự, Thread main sẽ chờ khi Thread con kết thúc nó mới tắt ứng dụng. :)
     
    beobot likes this.
  14. beobot

    beobot New Member

    Ai có tài liệu thực hành + code về đa luồng không ?
    Đang học cái này mà mù mờ quá.
     
  15. beobot

    beobot New Member

    Thứ 1 :
    Mình viết đoạn code sau :
    PHP:
    public void run()
        {

                for(
    int i=0;i<10;i++)
                {
                    
    System.out.println(Thread.currentThread().getName()+":"+i);
                  
    // Thread.sleep(1000);
                
    }
            
    System.out.println("Da xong");

        }
        
    Mình không cho Thread.sleep(1000) vào thì kết quả như thế này :

    [​IMG]

    Tại sao kết quả lại như vậy ?
    Theo mình biết thì các thread được thực hiện song song , vậy thì kết quả sẽ được in ra xen kẽ lẫn nhau chứ .

    Thứ 2 :
    ở ví dụ trên
    [​IMG]

    Mình cho vào synchronized tương tự như ở dưới .

    [​IMG]

    code đây :

    PHP:
    class ThreadDemo extends Thread
    {

        @
    Override
        
    public synchronized void run()
        {

            try{
              
    String name =Thread.currentThread().getName();
              
    JavaApplication.soTien -=1000;
              
    System.out.println(name" rut 1000");
              
    Thread.sleep(1000);
              
    System.out.println(name " con lai "+JavaApplication.soTien);
            }catch(
    InterruptedException ex)
            {

            }
      
        }

    }
    public class 
    JavaApplication  {

          public static 
    int soTien =2000;
        
    /**
        * @param args the command line arguments
        */
        
    public static void main(String[] argsthrows InterruptedException {
            
    // TODO code application logic here

            
    Thread t1 =new ThreadDemo();
            
    t1.setName("A");
            
    Thread t2 =new ThreadDemo();
            
    t2.setName("B");
            
    t1.start();
            
    t2.start();
     
        }
    }
    Nhưng tại sao kết quả lại là :

    [​IMG]

    ai giải thích giùm với.
     
  16. kim

    kim Thành viên VIP Staff Member

    Đoạn code thứ 1 bạn
    PHP:
    public void run()
        {

                for(
    int i=0;i<10;i++)
                {
                    
    System.out.println(Thread.currentThread().getName()+":"+i);
                  
    // Thread.sleep(1000);
                
    }
            
    System.out.println("Da xong");

        }
        
    Kết quả này hoàn toàn đúng, cho dù bạn có thêm sleep(1000); thì kết quả vẫn không thay đổi (tức dòng "Da xong" vẫn in vào thời điểm cuối cùng).
    Lý do chính là đây không phải là một Thread, mà đây là một đoạn code nhỏ trong Thread, như vậy nó sẽ thực hiện bình thường (tức chạy từ trên xuống dưới) cho tới khi hết toàn tất công việc, dĩ nhiên vòng for phải thực hiện xong mới được in ra dòng thông báo kia.

    Bài thứ 2: Tuy rằng bài này bạn chạy 2 luồng riêng biệt nhưng bạn lại không cho chúng sử dụng chung tài nguyên, việc này dẫn đến 2 Thread mà bạn tạo chẳng có thằng nào tranh chấp tài nguyên cả, và dĩ nhiên chẳng có tranh chấp thì tài nguyên sẽ bị thất thoát như bình thường.
    Nếu bạn để ý kỹ bạn sẽ thấy mình cố tình cho chúng sử dụng chung một tài nguyên
    [​IMG]

    Kiểm tra lại nhé bạn, vấn đề này dễ gây hiểu lầm lắm đó!
     
    KiraArus likes this.
  17. PSAleader

    PSAleader New Member

    code error!
     
  18. ForumModerator

    ForumModerator enforcement Of Rules

    Spam à? Không biết là nói phần nào, nói cho cái gì và nói về vấn đề gì ;;)
     
  19. PSAleader

    PSAleader New Member

    Hì! Không phải spam đâu! Lúc sang ngồi học đến phần Thread! code theo mà toàn báo lỗi! ức chế nên làm phát thế! hì hì!
    Bài viết hay!
     
  20. art_mu

    art_mu New Member

    hi, mình có thắc mắc này mong các bạn giúp mình
    là trên ví dụ synchronized thì mình tạo thêm thread là con và đổi salary = 4000 .
    thì cho thread con chạy là : con.start(); mình muốn khi run thì in ra theo thứ tự :
    Salary: 4000
    Thread cua vo rut 1000
    Thread cua vo hien thi tai khoan la : 3000
    Thread cua vo vua dang xuat [X]
    thread cua chong rut 1000
    thread cua chong hien thi tai khoan la : 2000
    thread cua chong vua dang xuat [X]
    thread cua con rut 1000
    thread cua con hien thi tai khoan la : 1000
    thread cua con vua dang xuat [X]
    thì phải synchronized làm sao để có thứ tự như trên...? (vo đến chong rồi mới đến con)
    hi hi ,cho mình xin cám ơn trước .:)
     

Chia sẻ trang này

Loading...