2011-10-20 33 views
6

Tôi đang tự hỏi về kết quả của decrementing không an toàn/incrementing trong chủ đề java, do đó chương trình của tôi:Chủ đề giảm/giảm không an toàn - tại sao chủ yếu là tích cực?

lớp chính:

public class Start { 

    public static void main(String[] args) { 

     int count = 10000000, pos = 0, neg = 0, zero = 0; 

     for (int x=0; x<10000; x++) { 

      Magic.counter = 0; 

      Thread dec = new Thread(new Magic(false, count)); 
      Thread inc = new Thread(new Magic(true, count)); 

      dec.start(); 
      inc.start(); 

      try { 
       inc.join(); 
       dec.join(); 
      } catch (InterruptedException e) { 
       System.out.println("Error"); 
      } 

      if (Magic.counter == 0) 
       zero++; 
      else if (Magic.counter > 0) 
       pos++; 
      else 
       neg++; 
     } 

     System.out.println(Integer.toString(neg) + "\t\t\t" + Integer.toString(pos) + "\t\t\t" + Integer.toString(zero)); 
    } 
} 

Chủ đề lớp:

public class Magic implements Runnable { 

    public static int counter = 0; 

    private boolean inc; 
    private int countTo; 

    public Magic(boolean inc, int countTo) { 
     this.inc = inc; 
     this.countTo = countTo; 
    } 

    @Override 
    public void run() { 

     for (int i=0;i<this.countTo;i++) { 

      if (this.inc) 
       Magic.counter++; 
      else 
       Magic.counter--; 
     } 

    } 
} 

tôi đã chạy chương trình vài lần, và luôn luôn nhận được kết quả tích cực hơn nhiều sau đó tiêu cực. Tôi cũng đã cố gắng thay đổi trật tự của chủ đề nào bắt đầu nhưng điều này không thay đổi gì cả. Một số kết quả:

Number of results < 0 | Number of results > 0 | Number of results = 0 

1103    8893    4 
3159    6838    3 
2639    7359    2 
3240    6755    5 
3264    6728    8 
2883    7112    5 
2973    7021    6 
3123    6873    4 
2882    7113    5 
3098    6896    6 
+0

Bạn có thể muốn làm cho Magic.counter biến không ổn định, nếu không hoạt động có thể được sắp xếp lại và/hoặc giá trị của nó có thể được lưu trữ cho mỗi chủ đề trong một thanh ghi. – prunge

Trả lời

6

Tôi đặt cược bạn sẽ thấy những hành vi hoàn toàn ngược lại với sự thay đổi sau đây (có nghĩa là, đảo ngược các ngành mà không thay đổi bất cứ điều gì khác):

if (this.inc) 
    Magic.counter--; // note change, and lie about `this.inc` 
else 
    Magic.counter++; 

Nếu đúng, những gì có thể điều này cho biết điều này cho biết về các tương tác luồng?

Bây giờ, để giải trí, hãy làm cho Magic.counter dễ bay hơi - [cách] các kết quả thay đổi như thế nào?

Điều gì về việc xóa volatile và xung quanh if/else bằng một số lock? (A lock đảm bảo một hàng rào bộ nhớ đầy đủ và thiết lập một khu vực quan trọng. Nó phải luôn mang lại kết quả hoàn hảo.)

Happy coding.


Những điều cần xem xét:

  1. Mã này chỉ nhìn vào nhỏ hơn hoặc lớn hơn số không, không phải tổng thể trôi dạt/biến thể: a +1 hoặc -1 là tất cả những gì cần thiết để tip quy mô. (Nó có thể hữu ích hơn để mở rộng dữ liệu được thu thập.)
  2. Phải mất nhiều thời gian hơn để thực hiện nhánh "khác" như là một bước nhảy được yêu cầu; bình thường, đây không phải là vấn đề, nhưng hơn 10 triệu chu kỳ ... một hoặc hai là không nhiều.
  3. Thiếu hàng rào dễ bay hơi/bộ nhớ cho nhiều lee-way trong khả năng hiển thị biến số Magic.counter. (Tôi tin rằng một JVM phù hợp thực sự có thể mang lại kết quả tồi tệ hơn nhiều ...)
  4. Các nhà điều hành ++-- vốn không phải là nguyên tử.
  5. Xen kẽ chủ đề thường "không xác định"; ít hơn nếu thực hiện trên nhiều lõi.
+0

Thay đổi thứ tự của Magic.counter -/++ "giúp" một chút, nhưng nó không chính xác đối diện :) của nó như 5,5k tiêu cực, 4,5k tích cực – Nazin

+0

@Nazin Thú vị, cảm ơn báo cáo lại :) Một trong những các vấn đề là ++ và - không phải là nguyên tử và các luồng được xen kẽ theo cách mà đọc/ghi được "tách" (wrt. hoạt động nguyên tử và giá trị hiển thị giữa các luồng). 'Biến động' sẽ giúp hiển thị giá trị, nhưng không đủ để đảm bảo một vùng quan trọng (và do đó nguyên tử) hoạt động' ++/- '. –

+0

@Nazin Có lẽ ai đó có nhiều chi tiết phức tạp hơn về triển khai JVM [cụ thể] có thể biết tại sao '++' và' --' dường như không hoàn toàn đối xứng ở khía cạnh này. Tốt nhất tôi có thể cung cấp ở cấp độ này là "non-deterministic" ;-) –

0

Nói chung, điều này là do cách thức hoạt động của mô hình bộ nhớ Java. Bạn đang truy cập các biến được chia sẻ trong hai chuỗi riêng biệt mà không cần đồng bộ hóa. Không phải là biến khai báo dễ bay hơi, cũng không phải bạn đang chạy các hoạt động nguyên tử. Sự vắng mặt của các biến phối hợp và nguyên tử hoặc biến động sẽ dẫn đến tối ưu hóa nội bộ của mã luồng được thực hiện bởi JVM khi thực hiện. Ngoài ra, các biến không dễ bay hơi không vượt qua hàng rào bộ nhớ (tức là synchronized) sẽ dẫn đến các giá trị được lưu trong mỗi luồng - và do đó có hai bản sao chủ đề xung đột trong bộ đệm của chúng.

Do không có mô hình nhất quán tuần tự trong Java, tối ưu hóa thời gian chạy phức tạp và đặc thù của JVM đã sử dụng và hệ thống cơ bản (đơn hoặc đa lõi, siêu phân luồng), không thể dự đoán kết quả theo định thức - chỉ vì nó đang vi phạm một số quy ước đa luồng cho mô hình ngôn ngữ Java. Việc chạy chính xác cùng một mã trên cùng một máy vẫn có thể dẫn đến kết quả tương tự, nhưng do tác động của việc lập lịch trình chuỗi, việc sử dụng CPU của các quá trình hệ điều hành khác, v.v ... chúng sẽ không giống chính xác.

Dưới đây là một số tài nguyên về JMM: http://www.cs.umd.edu/~pugh/java/memoryModel/

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