2016-02-28 17 views
8

Gần đây tôi đã tranh cãi với một người bạn trên một mã như thế này:Tầm nhìn của gán để biến trong Java

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
/** 
* See memory consistency effects in a Java Executor. 
*/ 
public class PrivateFieldInEnclosing { 
    private long value; 
    PrivateFieldInEnclosing() {} 
    void execute() { 
     value = initializeValue(); 
     ExecutorService executor = Executors.newCachedThreadPool(); 
     executor.submit(new Y()); 
    } 

    class Y implements Runnable { 
     @Override 
     public void run() { 
      System.out.println(value); 
     } 
    } 

    private long initializeValue() { 
     return 20; 
    } 

    public static void main(String[] args) { 
     new PrivateFieldInEnclosing().execute(); 
    } 
} 

Tôi cho rằng nó có thể là value thể được xem như 0 trong Y, bởi vì không có đảm bảo rằng bài tập value = initializeValue() hiển thị trong chuỗi của người thi hành. Tôi nói anh ta cần phải làm cho value một trường dễ bay hơi.

Anh ta mâu thuẫn với tôi, và nói rằng vì đó là một trường cá thể riêng với giá trị được gán trước khi tạo chuỗi, khi đó giá trị sẽ hiển thị.

Tôi đã xem xét https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 nhưng tôi không thể đặt ngón tay của mình lên chính xác những gì tôi có thể sử dụng làm sự ủng hộ cho tuyên bố của mình. Ai giúp tôi với? Cảm ơn!

Trả lời

9

Nó không liên quan cho dù đó là riêng tư hay không. hiệu ứng nhất quán

Memory:: Có gì liên quan này là Actions trong một thread trước khi nộp một đối tượng Runnable một Executor xảy ra-trước khi thực hiện nó bắt đầu, có lẽ trong thread khác.

Từ Executor docs. Điều đó có nghĩa là bất cứ điều gì bạn làm trước khi cuộc gọi đến submit hiển thị trong lần chạy. Bạn thậm chí có thể làm điều đó sau khi xây dựng trình thực thi, và trong trường hợp cụ thể này thậm chí không quan trọng khi chuỗi thực thi thực sự bắt đầu vì phương thức submit cung cấp sự bảo đảm rất mạnh mẽ.

Đây là một trong những tính năng làm cho gói java.util.concurrent rất hữu ích.

1

Bạn của bạn sẽ chính xác. NẾU khởi tạo biến là trước khi một cuộc gọi đến Thread.start theo thứ tự chương trình, bởi JLS 17.4.5 nó xảy ra trước Thread.start. Sự bắt đầu của thread cũng xảy ra trước khi hành động đầu tiên trong chuỗi. Do đó, điều này cũng xảy ra trước khi gọi tới số doStuffWithValue.

Trường hợp cụ thể này không thể được bao phủ bởi JLS do sử dụng Executor: bạn không biết khi nào nó gọi Thread.start cho các chủ đề mà nó sử dụng. Nhưng từ here, bạn có thể đọc rằng gọi số submit sẽ đảm bảo cho bạn giống như Thread.start: Hành động trong một luồng trước khi gửi Runnable đến Executor xảy ra trước khi bắt đầu thực hiện.

Do xảy ra trước khi chuyển đổi, việc khởi tạo biến xảy ra trước doStuffWithValue. Các bit về biến là một trường cá thể riêng là không liên quan mặc dù.

+0

Trong trường hợp này, điều này thậm chí không quan trọng khi chuỗi thực sự bắt đầu vì 'bảo đảm' gửi' xảy ra trước đó. Vì vậy, ngay cả khi việc triển khai thực thi của bạn sử dụng một số loại nhóm thread điên đã được khởi động trước khi khởi động JVM, bạn vẫn an toàn miễn là việc thực thi thực thi tuân theo đặc tả API. –

+0

Đúng, nhưng tôi đang chọn để bảo vệ đối số như được trình bày trong câu hỏi - đó là, từ nguyên tắc đầu tiên, bỏ qua thực tế là người thi hành đưa ra nhiều bảo đảm hơn. Câu trả lời của bạn giải thích quan điểm của lớp thực thi đủ tốt. – Joni

+0

@Joni Tôi không chắc chắn. Nếu luồng chạy phương thức 'execute()' thực hiện một cái gì đó giống như 'new Thread (new Y()).bắt đầu() ', sau đó tôi không nghĩ rằng' giá trị' có thể nhìn thấy trong bắt đầu 'Runnable' mà không có' giá trị' là 'dễ bay hơi'. Vì vậy, các bảo đảm xảy ra trước đó của các Executors được chỉ ra bởi @SergeyTachenov là cần thiết trong trường hợp này. Không? –

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