2013-08-09 37 views
6

Tôi có một máy chủ nào như thế này:An toàn chủ đề nhưng nhanh chóng truy cập vào biến "cuối cùng cuối cùng"?

class Server { 

    private WorkingThing worker; 

    public void init() { 
     runInNewThread({ 
      // this will take about a minute 
      worker = new WorkingThing(); 
     }); 
    } 

    public Response handleRequest(Request req) { 
     if (worker == null) throw new IllegalStateException("Not inited yet"); 
     return worker.work(req); 
    } 

} 

Như bạn có thể thấy, có đề xử lý các yêu cầu và một sợi initing máy chủ. Yêu cầu có thể đến trước khi kết thúc, do đó có séc với IllegalStateException.

Bây giờ, để làm cho chủ đề này an toàn (vì vậy yêu cầu bộ xử lý không nhìn thấy cũ, null -valued, phiên bản worker ngay sau init), tôi phải làm cho công nhân dễ bay hơi, đồng bộ hóa trên đó hoặc một số như là.

Tuy nhiên, sau khi quá trình init hoàn tất, worker sẽ không bao giờ thay đổi nữa, do đó, nó sẽ hiệu quả cuối cùng. Do đó, nó có vẻ như bất kỳ tranh chấp khóa có thể xảy ra sẽ là một sự lãng phí. Vậy, điều hiệu quả nhất tôi có thể làm ở đây là gì?

Bây giờ tôi biết nó không thực sự quan trọng trong một ý nghĩa thực tế (với tất cả các nâng nặng của đọc một yêu cầu mạng, vv, một vấn đề khóa duy nhất là gì?), Nhưng tôi muốn biết sự tò mò .

+0

sử dụng từ khóa 'đồng bộ hóa 'chi phí rất nhiều chu kỳ. Tôi nghĩ cách tốt nhất là biến 'worker' thành' static'. Bằng cách này nó sẽ phân bổ một không gian dành riêng trong bộ nhớ, và tôi nghĩ rằng các chủ đề sẽ luôn luôn nhìn vào không gian đó. Chỉ là một ý nghĩ, có thể không chính xác. –

+0

Việc gọi trình chủ 'worker.work (req)' có an toàn không? Tôi chỉ yêu cầu đảm bảo rằng bạn chỉ yêu cầu chặn các chuỗi gọi cho đến khi 'worker' được khởi tạo. Đúng không? –

+0

@ViktorSeifert Có, một khi 'nhân viên' ở đó, nó an toàn. –

Trả lời

3

Lưu ý về biến động: đánh dấu biến dễ bay hơi rẻ hơn sử dụng đồng bộ hóa (không liên quan đến khóa) và thường rẻ đủ để bạn không chú ý. Đặc biệt, trên kiến ​​trúc x86, đọc một biến biến động không tốn nhiều tiền hơn việc đọc một biến không biến động. Tuy nhiên văn bản cho một biến động là tốn kém hơn và thực tế là biến là dễ bay hơi có thể ngăn chặn một số tối ưu hóa trình biên dịch.

Vì vậy, việc sử dụng dễ bay hơi có lẽ là tùy chọn cung cấp cho bạn tỷ lệ hiệu suất/độ phức tạp tốt nhất trong kịch bản của bạn.

Bạn không có nhiều lựa chọn thay thế. Cuối cùng nó sôi xuống để đảm bảo một ấn phẩm an toàn của nhân viên của bạn. Và thành ngữ xuất bản an toàn bao gồm:

  • initialising trường hợp từ một initialiser tĩnh
  • đánh dấu tham chiếu đến dụ như thức
  • đánh dấu tham chiếu đến dụ như dễ bay hơi
  • đồng bộ hóa tất cả các truy cập

Trong trường hợp của bạn, chỉ có hai tùy chọn cuối cùng khả dụng và sử dụng biến động hiệu quả hơn.

+0

Ồ, tôi nghĩ rằng dễ bay hơi là ít hoặc nhiều cú pháp đường cho "cung cấp cho biến này một khóa và luôn luôn đồng bộ hóa trên nó khi truy cập biến này". Rất vui khi biết điều đó không phải như vậy. –

+0

Lưu ý rằng khởi tạo tĩnh diễn ra trong một khối đồng bộ; đó là cách mức độ hiển thị (và các thuộc tính khác) được đảm bảo. – erickson

1

Bạn nên khai báo công nhân là dễ bay hơi.

Bởi vì hai lý do

  1. nếu bạn không khai báo nó như là ổn định hơn do sắp xếp lại và tầm nhìn ảnh hưởng bạn có thể nhìn thấy tài liệu tham khảo không null để không đầy đủ xây dựng Worker Object. và do đó bạn có thể có các hiệu ứng không mong muốn. Điều này sẽ không xảy ra nếu bạn làm cho nhân viên của bạn bất biến bằng cách sử dụng tất cả các biến số cuối cùng là .

  2. Về lý thuyết, có thể chủ đề chính của bạn có thể không thấy tham chiếu không rỗng của đối tượng công nhân trong một thời gian dài. Vì vậy, tránh nó.

Vì vậy, kết luận nếu nhân viên không thay đổi được so với điểm 2, bạn nên làm cho nó dễ bay hơi. Luôn tránh các kết quả không thể đoán trước. Tuyên bố nó dễ bay hơi sẽ chăm sóc những vấn đề này.

0

Tôi muốn khám phá bằng cách sử dụng ava.util.concurrent.atomic.AtomicReference nếu nó rỗng, ném hoặc chờ và thử lại (nếu init đủ nhanh).

1

Sử dụng khóa đơn giản cho các yêu cầu ban đầu:

public synchronized void init() { 
    if(worker!=null) return; 
    runInNewThread({ 
     synchronized(Server.this){ 
      worker = new WorkingThing(); 
      Server.this.notify(); 
     } 
    }); 
    this.wait(); 
} 

public Response handleRequest(Request req) { 
    if (worker == null) synchronized(this) { 
     this.wait(); 
    } 
    return worker.work(req); 
} 

này hoạt động vì có những điểm đồng bộ hóa giữa các truy cập đến người lao động.

+1

Không bao giờ đợi bên ngoài vòng lặp. Nếu bạn bỏ lỡ tín hiệu thông báo, bạn có thể chờ đợi mãi mãi. Và có thể có nhiều hơn một luồng chờ => sử dụng notifyAll. Và không có điểm chờ đợi ở cuối init. – assylias

+0

Argh, phải - nên kiểm tra khóa kép trong khối handleRequest được đồng bộ hóa (này). –

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