6

Tôi khá mới đối với chương trình Scala và chức năng, và tôi thích ý tưởng sử dụng các đối tượng bất biến tôi có thể tránh được nhiều cạm bẫy an toàn. Một điều vẫn ám ảnh tôi, và đó là ví dụ cổ điển được sử dụng để dạy an toàn luồng - bộ đếm được chia sẻ. Tôi đã tự hỏi nếu nó có thể thực hiện một bộ đếm thread-an toàn (một truy vấn yêu cầu trong ví dụ này), sử dụng các đối tượng bất biến, và khái niệm chức năng, và tránh đồng bộ hóa hoàn toàn.Cách chức năng để thực hiện một bộ chia sẻ an toàn chủ đề

Vì vậy, để tham khảo đây là lần đầu tiên các phiên bản có thể thay đổi cổ điển của bộ đếm (xin lỗi cho biến thành viên công cộng, chỉ cho ngắn gọn trong những ví dụ)

thể thay đổi, không chủ đề phiên bản an toàn:

public class Servlet extends HttpServlet { 

    public int requestCount = 0; 

    @Override 
    public void service(ServletRequest req, ServletResponse res) throws ... { 
    requestCount++; //thread unsafe 
    super.service(req, res); 
    } 
} 

Biên Đổi, Classic chủ đề phiên bản an toàn: (hoặc vì vậy tôi hy vọng ...)

public class Servlet extends HttpServlet { 

    public volatile int requestCount = 0; 

    @Override 
    public void service(ServletRequest req, ServletResponse res) throws ... { 
    synchronized (this) { 
     requestCount++; 
    } 
    super.service(req, res); 
    } 
} 

Tôi đã tự hỏi nếu có một cách sử dụng các đối tượng bất biến, và biến biến động để đạt được an toàn thread mà không đồng bộ hóa.

Vì vậy, đây là nỗ lực ngây thơ của tôi. Ý tưởng là để có một đối tượng bất biến cho bộ đếm và chỉ thay thế tham chiếu đến nó bằng biến biến động. Cảm thấy cá, nhưng đáng để bắn.

Chủ:

public class Incrementer { 
    private final int value; 
    public Incrementer(final int oldValue) { 
    this.value = oldValue + 1; 
    } 

    public Incrementer() { 
    this.value = 0; 
    } 

    public int getValue() { 
    return value; 
    } 
} 

servlet Modified:

public class Servlet extends HttpServlet { 

    public volatile Incrementer incrementer = new Incrementer(); 

    @Override 
    public void service(ServletRequest req, ServletResponse res) throws ... { 
    incrementer = new Incrementer(incrementer.getValue()); 
    super.service(req, res); 
    } 
} 

Tôi có một cảm giác mạnh mẽ này cũng không phải là thread an toàn, như tôi đang đọc từ incrementer, và có thể nhận được giá trị cũ (ví dụ: nếu tham chiếu đã được thay thế bằng một chuỗi khác). Trong trường hợp nó thực sự không thread an toàn, sau đó tôi tự hỏi nếu có ở tất cả các "chức năng" cách để xử lý như một kịch bản truy cập mà không cần khóa/đồng bộ hóa.

Vì vậy, câu hỏi của tôi (s) là

  1. Là chủ đề này an toàn bởi bất kỳ cơ hội?
  2. Nếu có, tại sao?
  3. Nếu không, có cách nào để thực hiện bộ đếm như vậy mà không đồng bộ hóa không?

Mặc dù mã ví dụ trên là trong Java, trả lời trong Scala là tất nhiên cũng hoan nghênh

Trả lời

13

là chủ đề này an toàn bởi bất kỳ cơ hội?

Không, trừ khi bạn đã tạo đối tượng không thay đổi trong khối được đồng bộ hóa, đây không phải là chủ đề an toàn. Có cơ hội tạo ra một đối tượng bất biến bị hỏng trong điều kiện chủng tộc.

Và để đạt được cùng chức năng, bạn có thể sử dụng AtomicInteger để tránh đồng bộ hóa rõ ràng.

public class Servlet extends HttpServlet { 

    public AtomicInteger incrementer = new AtomicInteger (0); 

    @Override 
    public void service(ServletRequest req, ServletResponse res) throws ... { 
    int newValue = incrementer.incrementAndGet(); 
    super.service(req, res); 
    } 
} 
+0

Thú vị, tôi chắc chắn AtomicInteger sử dụng tính năng đồng bộ hóa nội bộ nên tôi thậm chí không coi đó là câu trả lời, nhưng nhìn vào mã nguồn có vẻ như nó đang sử dụng một số cách khác (mã gốc) để đạt được mục tiêu: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/atomic/AtomicInteger.java –

4

An toàn chủ đề từ bất biến trông giống như thế này.

val x = AtomicReference(Vector("salmon", "cod")) 

// Thread 1 
val y = x.get 
println(y(y.length-1)) 

// Thread 2 
x.getAndSet(x.get.tail) 

Nếu bạn làm việc một cách rõ ràng, bạn sẽ vô cùng bị cám dỗ để thay đổi Chủ đề 2, điều này có thể làm cho chỉ mục của Thread 1 thất bại. Hoặc bạn phải sao chép dữ liệu, điều này có thể rất tốn kém nếu bạn không có bộ sưu tập để sử dụng lại nhiều như thực tế (và nếu vectơ dài hơn). Hoặc bạn phải đồng bộ hóa các khối lớn trong cả hai chủ đề thay vì chỉ nhận nguyên tử và/hoặc thiết lập dữ liệu của bạn.

Bạn vẫn phải đồng bộ hóa bằng cách nào đó và bạn có thể phải xử lý các bản sao dữ liệu lỗi thời. Nhưng bạn không phải theo dõi ai có bản sao của cấu trúc dữ liệu nào và đồng bộ hóa mọi người như điên vì dữ liệu đó có thể thay đổi từ bên dưới bạn và ném ngoại lệ lên khắp nơi.

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