2009-05-15 19 views
7

Đoạn đơn giản của mã midlet (lớp Moo) dưới đây (sau khi trích đoạn) deadlocks (Ít nhất tôi giả định nó deadlocks sau khi đọc bài này trên chủ đề here).j2me mạng, chủ đề và deadlocks

tôi đã sao chép các trích đoạn có liên quan từ bài:

 

    String url = ... 
    Connection conn = null; 

    try { 
     conn = Connector.open(url); 
     // do something here 
    } 
    catch(IOException e){ 
     // error 
    } 
 

Gốc của vấn đề là việc ngăn chặn chất của() gọi mở. Trên một số nền tảng, hệ thống thực hiện kết nối thực tế dưới các nắp, trên cùng một chuỗi riêng biệt. Chặn chuỗi cuộc gọi cho đến khi chuỗi kết nối tạo kết nối. Đồng thời, hệ thống phụ bảo mật có thể yêu cầu người dùng xác nhận kết nối và chuỗi chỉ kết nối cho đến khi chuỗi sự kiện được xác nhận từ người dùng. Lỗi bế tắc xảy ra vì chuỗi sự kiện đã đợi chuỗi kết nối.

 

public class Moo extends MIDlet { 

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException { 
     // TODO Auto-generated method stub 

    } 

    protected void pauseApp() { 
    } 

    protected void startApp() throws MIDletStateChangeException { 
     Display display = Display.getDisplay(this); 
     MyCanvas myCanvas = new MyCanvas(); 
     display.setCurrent(myCanvas); 
     myCanvas.repaint(); 

    } 

    class MyCanvas extends Canvas { 

     protected void paint(Graphics graphics) { 
       try { 
         Image bgImage = Image.createImage(getWidth(), getHeight()); 

         HttpConnection httpConnection = (HttpConnection) Connector 
             .open("http://stackoverflow.com/content/img/so/logo.png"); 
         Image image = Image.createImage(httpConnection 
             .openInputStream()); 
         bgImage.getGraphics().drawImage(image, 0, 0, 0); 
         httpConnection.close(); 

         graphics.drawImage(bgImage, 0, 0, 0); 
       } catch (IOException e) { 
         e.printStackTrace(); 
       } 
     } 

    } 

} 
 

Ai đó có thể xin vui lòng cho tôi biết thế nào gọi hệ thống thread được thực hiện ở đây (sự kiện và thông báo chủ đề) và chuỗi các sự kiện dẫn đến sự bế tắc. Tôi không rõ ràng những gì là các chủ đề liên quan ở đây dẫn đến bế tắc.

  1. Có tài liệu nào về mô hình chuỗi j2me không?
  2. Tôi có thể lấy nguồn cho các lớp hệ thống j2me ở đâu (Tôi muốn kiểm tra việc triển khai các lớp Kết nối)?

EDIT: Trong đoạn mã trên, tôi nhận được logic. Nhưng mã dưới đây ít nhất phải hoạt động đúng không? Điều này cũng deadlocks nơi tôi đang làm kết nối mạng trong một chủ đề riêng biệt.

 

public class Foo extends MIDlet { 

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException { 
     // TODO Auto-generated method stub 
    } 

    protected void pauseApp() { 
     // TODO Auto-generated method stub 
    } 

    protected void startApp() throws MIDletStateChangeException { 
     Display display = Display.getDisplay(this); 
     MyCanvas myCanvas = new MyCanvas(); 
     display.setCurrent(myCanvas); 
     myCanvas.repaint(); 
    } 

    class MyCanvas extends Canvas { 
     protected void paint(Graphics graphics) { 
      try { 
       Image bgImage = Image.createImage(getWidth(), getHeight()); 

       FetchImage fetchImage = new FetchImage(); 
       Thread thread = new Thread(fetchImage); 
       thread.start(); 

       thread.join(); 

       bgImage.getGraphics().drawImage(fetchImage.image, 0, 0, 0); 

       graphics.drawImage(bgImage, 0, 0, 0); 
      } catch (Exception e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

    public class FetchImage implements Runnable { 
     public Image image; 

     public void run() { 
      HttpConnection httpConnection; 
      try { 
       httpConnection = (HttpConnection) Connector 
         .open("http://10.4.71.200/stage/images/front/car.png"); 
       image = Image.createImage(httpConnection.openInputStream()); 
       httpConnection.close(); 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 

    } 
} 

Trả lời

9

Tôi có thể lấy nguồn cho các lớp hệ thống J2ME (Tôi muốn kiểm tra ra việc thực hiện các kết nối lớp) ở đâu?

Bạn không thể. Thực sự của nó nhà cung cấp phụ thuộc. Cách thức Nokia xử lý tình huống này có thể khác với Motorola.

Bài học bạn phải học là không tính toán tốn kém trong các cuộc gọi lại hệ thống vì điều đó có thể làm cho hệ thống không phản hồi. Vì vậy, đặt các hoạt động tốn thời gian trong một thread sepearate và luôn luôn trở về từ cuộc gọi trở lại càng sớm càng tốt.

Mặc dù bạn đã tạo một chuỗi riêng biệt trong ví dụ thứ hai của mình, bạn đợi cho đến khi hoàn thành nó bằng sơn() và cuối cùng là kết quả là không có luồng nào cả!

Một điều bạn có thể làm là

class MyCanvas extends Canvas { 

    Image image; 
    boolean imageFetchFailed; 

    protected void paint(Graphics g) { 
     if (image == null) { 
      fetchImage(); 
      g.drawString("Fetching...", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP) 

     } else if (imageFetchFailed) { 
      g.drawString("Failed to fetch image", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP) 
     } else { 
      g.drawImage(image, 0, 0, 0); 
     } 
    } 


    private void fetchImage() { 
     new Thread(new Runnable() { 
      public void run() { 
       HttpConnection httpConnection = null; 
       try { 
        httpConnection = (HttpConnection) Connector 
          .open("http://10.4.71.200/stage/images/front/car.png"); 
        image = Image.createImage(httpConnection.openInputStream()); 
       } catch (IOException e) { 
        e.printStackTrace(); 
        imageFetchFailed = true; 
       } 

       if (httpConnection != null) { 
        try { 
         httpConnection.close(); 
        } catch (IOException ignored) { 
        } 
       } 

       // Following will trigger a paint call 
       // and this time image wont be null and will get painted on screen 
       repaint();  
      } 
     }).start(); 
    } 
} 
+0

Câu hỏi và trả lời tuyệt vời, đặc biệt nếu bạn thực sự muốn một số rõ ràng với điều này luồng. – Sydwell

1

Vâng, vấn đề cơ bản là một số triển khai Java VM sử dụng các chủ đề Java tương tự để làm tất cả mọi thứ.

Một trong những điều đầu tiên bạn cần tìm hiểu về mô hình luồng của máy ảo là người đã phát triển nó.

Có một danh sách của J2ME được cấp phép ở đây: http://java.sun.com/javame/licensees/index.jsp

Từ thông tin đó, hãy cố gắng tìm ra bao nhiêu bài bản VM của bạn đang sử dụng. Hai mô hình thông thường hoặc là chạy tất cả các giải thích bytecode thành một chuỗi gốc hoặc chạy từng chuỗi java vào chủ đề riêng của nó.

Bước tiếp theo là thu thập thông tin về cách các API hệ điều hành cơ bản không đồng bộ. Khi phát triển VM, những người được cấp phép sẽ phải viết mã gốc để chuyển VM tới hệ điều hành. Bất kỳ Inter-Process Truyền thông hoặc sử dụng phương tiện truyền chậm (từ thẻ flash đến tín hiệu GPS) có thể được thực hiện bằng cách sử dụng một chuỗi riêng biệt có thể cho phép chuỗi thông dịch bytecode tiếp tục chạy trong khi hệ thống chờ một số dữ liệu.

Bước tiếp theo là nhận ra cách triển khai máy ảo kém. Thông thường, vấn đề của bạn xảy ra khi máy ảo chỉ sử dụng một chuỗi java nội bộ cho tất cả các phương thức callbacks trong thông số MIDP. Vì vậy, bạn không có cơ hội để phản ứng với các sự kiện bàn phím cho đến khi kết nối của bạn được mở nếu bạn cố gắng mở nó trong chuỗi java sai.

Tệ hơn nữa, bạn thực sự có thể ngăn không cho màn hình được làm mới vì Canvas.paint() sẽ được gọi trong cùng chuỗi java như javax.microedition.media.PlayerListener.playerUpdate() chẳng hạn.

Từ góc nhìn triển khai VM, quy tắc vàng là bất kỳ cuộc gọi lại nào bạn không kiểm soát (vì nó có thể kết thúc bằng mã "người dùng", như trình nghe) không thể được gọi từ cùng một chuỗi java bạn sử dụng. các cuộc gọi API chuẩn. Rất nhiều VM ra khỏi đó chỉ đơn giản là phá vỡ quy tắc đó, do đó Sun đề nghị các nhà phát triển JavaME làm việc xung quanh nó.

+0

Cảm ơn. Đã cập nhật câu hỏi dựa trên câu trả lời của bạn. – Abhi

1

Canvas.paint() là một event delivery method, có nghĩa là nó được gọi bởi chuỗi sự kiện hệ thống.

Giả sử trên một hệ thống, cả việc xử lý sự kiện xác nhận cuộc gọi và xác nhận người dùng Canvas.paint() được thực hiện bằng chuỗi sự kiện UI (UT).

Bây giờ, khi UT bị chặn bên trong Canvas.paint() bởi Connector.open(), UT chắc chắn không thể xử lý sự kiện sắp tới, trong trường hợp này là sự kiện xác nhận người dùng được kích hoạt bởi Connector.open() . UT không thể xử lý một sự kiện khác khi nó bị chặn bên trong mã ứng dụng của bạn.

Đó là lý do tại sao bế tắc xảy ra here, chuỗi kết nối đang chờ thứ gì đó sẽ không bao giờ xảy ra và chặn vĩnh viễn.

Nói chung, bạn không nên mong đợi cách chuỗi sự kiện hệ thống được triển khai và cố gắng quay lại từ phương thức xử lý sự kiện càng nhanh càng tốt. Nếu không, bạn có thể nhận được hiệu suất thấp hơn hoặc bế tắc như thế này.

+0

Cảm ơn. Tôi nghĩ rằng tôi sắp xếp nó ngay bây giờ. Làm thế nào bạn sẽ sắp xếp lại mã để làm cho nó hoạt động? Theo cách tôi thấy, hệ thống sẽ gọi phương thức vẽ sau khi hình ảnh được lấy từ mạng. Có cách nào tốt hơn để làm điều đó không? Có mô hình nào liên quan đến vấn đề tôi đang gặp phải và một giải pháp tiêu chuẩn cho nó không? Xin lỗi, tôi là người mới hoàn thành với J2ME và giao diện người dùng. – Abhi

+0

Manoj vừa cho bạn thấy một ví dụ điển hình, chú ý thread.join() vẫn bị chặn UT bên trong Canvas.paint(). Chỉ cần cố gắng quay lại từ các cuộc gọi lại sự kiện càng nhanh càng tốt và để chuỗi sự kiện xử lý sự kiện tiếp theo. – tingyu

0

Một số ý tưởng tốt, nhưng có vẻ như rằng có một điều kiện chủng tộc trong ví dụ Manoj của.

Có thể thực hiện nhiều cuộc gọi sơn trong khi đang tải xuống, khiến nhiều chủ đề được tạo tất cả tải xuống cùng một hình ảnh (Một ví dụ về cuộc gọi sơn bổ sung là khi lời nhắc kết nối HTTP bật lên).

Vì tất cả các cuộc gọi sơn được thực hiện trên cùng một chuỗi, chúng tôi có thể tránh đồng bộ hóa bằng cách kiểm tra và đặt cờ bên trong cuộc gọi sơn.Dưới đây là một nỗ lực tại một phiên bản cải tiến:

class MyCanvas extends Canvas { 

    Image image; 
    boolean imageDownloadStarted; 
    boolean imageFetchFailed; 

    protected void paint(Graphics g) { 
     g.fillRect(0, 0, g.getClipWidth(), g.getClipHeight()); 
     if (image == null) { 
      if (imageDownloadStarted) 
       return; 
      imageDownloadStarted = true; 
      fetchImage(); 
      g.drawString("Fetching...", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP); 

     } else if (imageFetchFailed) { 
      g.drawString("Failed to fetch image", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP); 
     } else { 
      g.drawImage(image, 0, 0, 0); 
     } 
    } 

    private void fetchImage() { 
     new Thread(new Runnable() { 

      public void run() { 
       try { 
        final HttpConnection httpConnection = (HttpConnection) Connector.open("http://stackoverflow.com/content/img/so/logo.png"); 
        try { 
         final InputStream stream = httpConnection.openInputStream(); 
         try { 
          image = Image.createImage(stream); 
         } finally { 
          stream.close(); 
         } 
        } finally { 
         httpConnection.close(); 
        } 
       } catch (IOException e) { 
        e.printStackTrace(); 
        imageFetchFailed = true; 
       } 

       repaint(); 
      } 
     }).start(); 
    } 
} 

Lưu ý việc sử dụng các thức từ khóa để tránh kiểm tra null và bế mạc rõ ràng của dòng trả về bởi openInputStream.