2015-05-27 18 views
5

Chạy mã này, tôi hy vọng nó sẽ tăng biến thử nghiệm trong 5 giây và sau đó kết thúc.Thời gian trong khi vòng lặp không kết thúc

import java.util.Timer; 
import java.util.TimerTask; 

public class Test { 
    private static boolean running; 

    public static void main(String[] args) { 
     long time = 5 * 1000;  // converts time to milliseconds 
     long test = Long.MIN_VALUE; 
     running = true; 

     // Uses an anonymous class to set the running variable to false 
     Timer timer = new Timer(); 
     timer.schedule(new TimerTask() { 
      @Override 
      public void run() { running = false; } 
     }, time); 

     while(running) { 
      test++; 
     } 

     timer.cancel(); 
     System.out.println(test); 
    } 
} 

Tuy nhiên khi tôi chạy chương trình không kết thúc (tôi giả sử, tôi đã cho nó một khoảng thời gian hợp lý). Tuy nhiên, nếu tôi thay đổi vòng lặp while thành

while(running) { 
     System.out.println(); 
     test++; 
    } 

Chương trình kết thúc trong khoảng thời gian dự kiến ​​(và in ra nhiều dòng). Tôi không hiểu. Tại sao hành vi này xảy ra?

+0

Điều gì sẽ xảy ra nếu bạn chạy 'biến động '? –

+0

Tôi vừa thử nghiệm mã của bạn bằng cách sử dụng JDK 7 với IntelliJ 11 và cả hai phiên bản đều kết thúc sau vài giây. Bạn đang sử dụng cái gì để chạy? –

+0

@AndyTurner wow, tôi chưa bao giờ bắt gặp từ khóa dễ bay hơi trước đây. Thats làm cho mã hoạt động. Cảm ơn! – DenverCoder9

Trả lời

4

Theo Java Memory Model không đảm bảo khi trường không bay hơi sẽ hiển thị từ chuỗi khác. Trong trường hợp của bạn, phương thức chính của bạn là JIT được biên dịch và trình biên dịch JIT giả định hợp lý rằng luồng hiện tại không sửa đổi biến số running trong một vòng lặp, sau đó chúng ta không nên đọc nó trên mỗi lần lặp và chuyển đổi vòng lặp thành vòng lặp vô hạn. Thậm chí còn có FindBugs warning về trường hợp này.

Khi bạn thêm một cuộc gọi System.out.println, dường như trình biên dịch JIT không thể inline nó, vì vậy nó không thể chắc chắn rằng phương pháp này không thay đổi biến running, do đó nó sẽ tắt tối ưu hóa lĩnh vực đọc. Tuy nhiên điều đó không nên được coi là giải pháp vấn đề: có thể các phiên bản Java mới hơn sẽ thông minh hơn và tối ưu hóa vòng lặp của bạn ngay cả khi bạn có System.out.println bên trong.

+0

Tôi chấp nhận câu trả lời này vì nó liên kết thông tin bổ sung. Cảm ơn! – DenverCoder9

2

Cho phép tiến gần hơn vào mã của bạn ...

Nếu bạn gỡ lỗi mã, mã sẽ hoạt động. Đó là bởi vì bạn sẽ chỉ thấy một luồng và không có bộ đệm. Java có thể sử dụng các cơ chế bộ đệm dựa trên luồng. Bạn cần phải đọc và ghi nó vào bộ nhớ chính.

Vì vậy, nếu bạn sử dụng volatile từ khóa trên biến đang chạy của mình, jvm sẽ thấy biến đó có thể chỉnh sửa bởi nhiều chuỗi và không được lưu vào bộ nhớ cache.

Vì vậy, trong giải pháp tình huống của bạn, hãy thêm volatile vào biến đang chạy của bạn.

2

Bạn đang cập nhật biến số running trong một luồng khác từ phương thức main của mình. Mô hình bộ nhớ Java là như vậy mà bạn không được bảo đảm để xem các cập nhật cho các biến không biến động trong các chủ đề khác nhau.

Giải pháp dễ nhất là tạo biến volatile: điều này buộc giá trị 'hiện tại' của biến sẽ được kiểm tra khi chuỗi truy cập.

giải pháp khác bao gồm sử dụng AtomicBoolean thay vì boolean, và gói quyền truy cập vào running trong lẫn nhau synchronized khối (ví dụ: mã truy cập running được đồng bộ trên màn hình giống như mã số đó cập nhật nó).

Tôi đặc biệt khuyên bạn nên chọn một bản sao của Java Concurrency In Practice, mô tả chi tiết vấn đề này.

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