2013-07-01 39 views
8

Tôi đang cố gắng kiểm tra cách chờ/thông báo hoạt động trong java.Chờ()/thông báo() đồng bộ hóa

Code:

public class Tester { 
    public static void main(String[] args) { 
     MyRunnable r = new MyRunnable(); 
     Thread t = new Thread(r); 
     t.start(); 
     synchronized (t) { 
      try { 
       System.out.println("wating for t to complete"); 
       t.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

class MyRunnable implements Runnable { 
    public void run() { 
     System.out.println("entering run method"); 
     synchronized (this) { 
      System.out.println("entering syncronised block"); 
      notify(); 
      try { 
       Thread.currentThread().sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println("leaving syncronized block"); 
     } 
     System.out.println("leaving run method"); 
    } 
} 

Output Returned

wating for t to complete 
entering run method 
entering syncronised block 
//sleep called 
leaving syncronized block 
leaving run method 
wait over 

tôi đã mong đợi khi thông báo() được thực thi chờ đợi sẽ kết thúc & System.out.println("wait over"); sẽ được in ra. Nhưng có vẻ như nó chỉ được in khi t hoàn thành run() của nó.

+5

Bạn không đồng bộ hóa trên cùng một đối tượng – MadProgrammer

+0

MyRunnable.this == r! = T –

+1

@ raul8 Chỉnh sửa câu hỏi và dán trong câu trả lời làm cho câu trả lời đúng không hợp lệ. Sẽ tốt hơn nếu bạn thêm một câu hỏi khác. –

Trả lời

9

màn Object khóa cần phải được thực hiện một tham chiếu duy nhất của khóa cùng ...

Trong ví dụ của bạn, bạn là waiting trên một thể hiện của các Thread, nhưng sử dụng notify từ Runnable. Thay vào đó, bạn nên sử dụng một đối tượng khóa chung duy nhất ... ví dụ

public class Tester { 

    public static final Object LOCK = new Object(); 

    public static void main(String[] args) { 
     MyRunnable r = new MyRunnable(); 
     Thread t = new Thread(r); 
     t.start(); 
     synchronized (LOCK) { 
      try { 
       System.out.println("wating for t to complete"); 
       LOCK.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public static class MyRunnable implements Runnable { 

     public void run() { 
      System.out.println("entering run method"); 
      synchronized (LOCK) { 
       System.out.println("entering syncronised block"); 
       LOCK.notify(); 
       try { 
        Thread.currentThread().sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       System.out.println("leaving syncronized block"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

Output ...

wating for t to complete 
entering run method 
entering syncronised block 
leaving syncronized block 
wait over 
leaving run method 

wait overleaving run method có thể thay đổi vị trí tùy thuộc vào kế hoạch đề.

Bạn có thể thử đặt chế độ ngủ bên ngoài khối synchronized. Điều này sẽ mở khóa màn hình cho phép phần wait để tiếp tục chạy (vì nó không thể bắt đầu cho đến khi khóa được giải phóng)

public static class MyRunnable implements Runnable { 

     public void run() { 
      System.out.println("entering run method"); 
      synchronized (LOCK) { 
       System.out.println("entering syncronised block"); 
       LOCK.notify(); 
       System.out.println("leaving syncronized block"); 
      } 
      try { 
       Thread.currentThread().sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
+0

cảm ơn ... nhưng tôi đã thử mã của bạn .. nhưng nó vẫn không hoạt động. Mã số được đề cập cập nhật – reiley

+0

Dường như làm việc được rồi. Bạn đang mong đợi điều gì? – MadProgrammer

+0

FYI '' Thread.sleep'' (cả hai hình thức) là một phương pháp tĩnh luôn đặt chuỗi gọi vào giấc ngủ; do đó '' Thread.currentThread(). sleep (1000) '' là ngữ nghĩa dư thừa và có thể gây hiểu nhầm (ví dụ: gọi '' t.sleep (1000) '' sẽ đặt chuỗi gọi vào giấc ngủ, không phải t). – kbolino

5

trả lời mã Cập nhật:

Từ Thread.sleep() javadoc:

Gây chủ đề hiện đang thực thi thành chế độ ngủ (tạm dừng thực thi) cho số được chỉ định của mili giây, tùy thuộc vào độ chính xác và độ chính xác của bộ hẹn giờ hệ thống và bộ lập lịch. Chuỗi không mất quyền sở hữu của bất kỳ màn hình nào.

Nếu bạn gọi Thread.sleep trong khi ở trong khối được đồng bộ, các chuỗi khác sẽ không thể nhập khối được đồng bộ hóa. Bạn không bao giờ nên làm nhiệm vụ tốn thời gian trong khi trong một khối đồng bộ để tránh điều này.

1

Lưu ý (như những người khác được chỉ ra là tốt) mà bạn phải sử dụng cùng một đối tượng để khóa/đồng bộ hóa trong cả hai chủ đề.

Nếu bạn muốn chủ đề chính của mình tiếp tục ngay lập tức sau khi notify được gọi, bạn phải từ bỏ khóa tạm thời. Nếu không, wait sẽ chỉ được gọi sau khi chuỗi thứ hai rời khỏi khối synchronized. Và nó không bao giờ là một ý tưởng tốt để giữ một khóa trong một tính toán chạy dài!

Một cách làm thế nào để đạt được là sử dụng wait(int) vào khóa thay vì sleep, vì wait phiên bản khóa đồng bộ hóa tạm thời:

public class Tester { 
    private static final Object lock = new Object(); 

    public static void main(String[] args) { 
     Thread t = new Thread(new MyRunnable()); 
     t.start(); 
     synchronized (lock) { 
      try { 
       System.out.println("wating for t to complete"); 
       lock.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    static class MyRunnable implements Runnable { 
     public void run() { 
      System.out.println("entering run method"); 
      synchronized (lock) { 
       System.out.println("entering syncronised block"); 
       lock.notify(); 
       try { 
        lock.wait(1000); // relinquish the lock temporarily 
       } catch (InterruptedException ex) { 
        System.out.println("got interrupted"); 
       } 
       System.out.println("leaving syncronized block"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

Tuy nhiên, sử dụng các nguyên thủy ở mức độ thấp có thể rất dễ bị lỗi và tôi 'd không khuyến khích sử dụng chúng. Thay vào đó, tôi khuyên bạn nên sử dụng nguyên thủy cấp cao của Java cho điều đó. Ví dụ, bạn có thể sử dụng CountDownLatch cho phép một thread chờ đợi cho đến khi chủ đề khác đếm ngược tới zero:

import java.util.concurrent.*; 

public class TesterC { 
    private static final CountDownLatch latch = new CountDownLatch(1); 

    public static void main(String[] args) { 
     Thread t = new Thread(new MyRunnable()); 
     t.start(); 

     System.out.println("wating for t to complete"); 
     try { 
      latch.await(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("wait over"); 
    } 

    static class MyRunnable implements Runnable { 
     public void run() { 
      System.out.println("entering run method"); 
      try { 
       latch.countDown(); 
       Thread.sleep(1000); 
      } catch (InterruptedException ex) { 
       System.out.println("got interrupted"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

Ở đây bạn không cần phải đồng bộ hóa bất cứ điều gì, chốt làm mọi thứ cho bạn. Có nhiều nguyên thủy khác mà bạn có thể sử dụng - các semaphores, một bộ trao đổi, các hàng đợi an toàn thread, vv. Explorer gói java.util.concurrent.

Có lẽ giải pháp thậm chí tốt hơn là sử dụng API cấp cao hơn, chẳng hạn như Akka cung cấp. Ở đó bạn làm việc với Actors hoặc Software transactional memory, có thể được soạn dễ dàng và dành cho bạn hầu hết các vấn đề tương tranh.

Các vấn đề liên quan