6

Cuốn sách cũng được đánh giá cao JCIP nói điều này về việc sử dụng ThreadLocal:Làm thế nào để sử dụng ThreadLocal giảm thể dùng lại

Nó rất dễ dàng để lạm dụng ThreadLocal bằng cách xử lý tài sản đề giam giữ nó như là một giấy phép sử dụng các biến toàn cầu hoặc như một phương tiện tạo các đối số phương thức "ẩn". Biến chủ đề địa phương có thể làm giảm khả năng sử dụng lại và giới thiệu khớp nối ẩn giữa các lớp và do đó nên được sử dụng cẩn thận.

Có nghĩa là gì khi nói rằng các biến Thread-local có thể giảm khả năng sử dụng lại và giới thiệu các mối nối ẩn giữa các lớp?

+0

Tôi đã chỉnh sửa câu trả lời của mình bằng một ví dụ mã đơn giản. – Tudor

Trả lời

10

Chúng giảm khả năng sử dụng lại theo cách tương tự mà biến toàn cầu thực hiện: khi tính toán của phương pháp phụ thuộc vào trạng thái bên ngoài phương thức, nhưng không được chuyển thành tham số (ví dụ: trường lớp), phương pháp của bạn ít sử dụng được hơn, bởi vì nó được kết hợp chặt chẽ với trạng thái của đối tượng/lớp mà nó cư trú (hoặc tệ hơn, trên một lớp khác hoàn toàn).

Chỉnh sửa: Ok, đây là ví dụ để làm rõ hơn. Tôi đã sử dụng ThreadLocal chỉ vì lợi ích của câu hỏi, nhưng nó áp dụng cho các biến toàn cầu nói chung. Giả sử tôi muốn tính tổng của N số nguyên đầu tiên song song trên một số luồng. Chúng tôi biết rằng cách tốt nhất để làm điều đó là tính toán số tiền địa phương cho mỗi luồng và tổng hợp chúng ở cuối. Đối với một số lý do chúng tôi quyết định rằng phương pháp call của mỗi Task sẽ sử dụng một biến ThreadLocal sum được định nghĩa trong một lớp học khác nhau như một toàn cầu (tĩnh) biến:

class Foo { 
    public static ThreadLocal<Long> localSum = new ThreadLocal<Long>() { 
     public Long initialValue() { 
      return new Long(0);   
     } 
    }; 
} 

class Task implements Callable<Long> { 

    private int start = 0; 
    private int end = 0;  

    public Task(int start, int end) { 
     this.start = start; 
     this.end = end; 
    } 

    public Long call() { 
     for(int i = start; i < end; i++) { 
      Foo.localSum.set(Foo.localSum.get() + i); 
     } 
     return Foo.localSum.get(); 
    } 
} 

Mã này hoạt động một cách chính xác và cung cấp cho chúng ta những giá trị kỳ vọng của tổng số toàn cầu, nhưng chúng tôi nhận thấy rằng lớp Task và phương pháp call của chúng tôi hiện được ghép nối hoàn toàn với lớp Foo. Nếu tôi muốn sử dụng lại lớp Task trong một dự án khác, tôi cũng phải di chuyển lớp Foo nếu không mã sẽ không biên dịch.

Mặc dù đây là một ví dụ đơn giản phức tạp về mục đích, bạn có thể thấy các nguy cơ của các biến toàn cầu "ẩn". Nó cũng ảnh hưởng đến khả năng đọc, vì ai đó đọc mã sẽ phải tìm kiếm lớp Foo và xem định nghĩa của Foo.localSum là gì. Bạn nên giữ cho các lớp học của bạn càng khép kín càng tốt.

+0

@Subhra - câu trả lời của Tudor có đủ rõ ràng không? Tại sao bạn đặc biệt cần 'mã ví dụ'? –

+0

@Subhra: Tôi đã chỉnh sửa bằng một ví dụ mã đơn giản. – Tudor

3

Một ThreadLocal được khai báo cho mỗi thread - thường là một lĩnh vực được khai báo mỗi đối tượng của lớp đó - Bắt đầu từ đây - có thể là một toàn bộ rất nhiều thứ có thể đi sai nếu ThreadLocal được lạm dụng.

Nếu chuỗi chạy qua nhiều đối tượng, (một trong hai lớp đơn hoặc nhiều lớp), thì ThreadLocal được sử dụng bởi chuỗi này là cùng một ví dụ trên tất cả các trường hợp này. Đây là BG liên kết đang nói đến. Khoảnh khắc có khớp nối - việc tái sử dụng trở nên khó khăn và dễ bị lỗi.

+0

Các tài liệu Java về ThreadLocal nói rằng thường ThreadLocals nên được tư nhân tĩnh nhưng 'tĩnh' không được thực thi bởi ThreadLocal. Vì vậy, về mặt kỹ thuật ThreadLocal có thể cho mỗi thread-per instance. Tuy nhiên đoạn thứ hai của bạn không trả lời câu hỏi khá tốt. – Inquisitive

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