2012-04-11 6 views
6

Biến nào trong ThreadLocal hoặc biến cục bộ trong Runnable sẽ được ưu tiên. Vì lý do hiệu suất. Tôi hy vọng sử dụng biến cục bộ sẽ cung cấp nhiều cơ hội hơn cho bộ nhớ đệm cpu, v.v.Biến ThreadLocal vs local trong Runnable

Cảm ơn bạn đã trợ giúp.

+0

Nếu bạn quan tâm hoàn toàn đến hiệu suất của ThreadLocal so với biến địa phương, câu hỏi này có rất nhiều bài đọc tốt: http://stackoverflow.com/questions/609826/performance-of-threadlocal-variable – Alex

Trả lời

14

Biến nào trong số ThreadLocal hoặc biến cục bộ trong Runnable sẽ được ưu tiên.

Nếu bạn có một biến được khai báo bên trong lớp của chủ đề (hoặc Runnable) sau đó một biến địa phương sẽ làm việc và bạn không cần ThreadLocal.

new Thread(new Runnable() { 
    // no need to make this a thread local because each thread already 
    // has their own copy of it 
    private SimpleDateFormat format = new SimpleDateFormat(...); 
    public void run() { 
     ... 
     // this is allocated per thread so no thread-local 
     format.parse(...); 
     ... 
    } 
}).start(); 

ThreadLocal được sử dụng để lưu trạng thái trên cơ sở mỗi chủ đề khi bạn đang thực thi mã chung. Ví dụ: SimpleDateFormat là (không may) không an toàn chỉ vì vậy nếu bạn muốn sử dụng, bạn sẽ cần lưu trữ một tài khoản theo số ThreadLocal để mỗi chuỗi nhận được phiên bản riêng của định dạng.

private final ThreadLocal<SimpleDateFormat> localFormat = 
    new ThreadLocal<SimpleDateFormat>() { 
     @Override 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat(...); 
     } 
    }; 
... 
// if a number of threads run this common code 
SimpleDateFormat format = localFormat.get(); 
// now we are using the per-thread format (but we should be using Joda Time :-) 
format.parse(...); 

Ví dụ về điều này là cần thiết là trình xử lý yêu cầu web. Các chủ đề được phân bổ trong đất Jetty (ví dụ) trong một số loại hồ bơi nằm ngoài tầm kiểm soát của chúng tôi. Một yêu cầu web đến trong đó phù hợp với con đường của bạn để Jetty gọi trình xử lý của bạn. Bạn cần phải có một đối tượng SimpleDateFormat nhưng vì những hạn chế của nó, bạn phải tạo một đối tượng cho mỗi luồng. Đó là khi bạn cần ThreadLocal. Khi bạn đang viết mã reentrant có thể được gọi bởi nhiều chủ đề và bạn muốn lưu trữ một cái gì đó cho mỗi chủ đề.

Nếu bạn muốn chuyển đối số vào Runnable thì bạn nên tạo lớp của riêng mình và sau đó bạn có thể truy cập hàm tạo và chuyển đối số.

new Thread(new MyRunnable("some important string")).start(); 
... 
private static class MyRunnable implements { 
    private final String someImportantString; 
    public MyRunnable(String someImportantString) { 
     this.someImportantString = someImportantString; 
    } 
    // run by the thread 
    public void run() { 
     // use the someImportantString string here 
     ... 
    } 
} 
+0

Rất tốt, nhưng Tôi sẽ thay đổi ví dụ ThreadLocal để khởi tạo localFormat với một lớp con ẩn danh ghi đè initialValue, vì đó là mẫu sử dụng điển hình. – Alex

+0

bạn hiểu lầm tôi.Xem mã ví dụ trong Javadoc cho ThreadLocal - nó cho thấy cách đặt đúng mã khởi tạo cho biến cho mỗi luồng vào một vị trí chung để nó được khởi tạo cho mỗi chuỗi gọi trước khi được trả về bởi get(), bất kể nó ở đâu gọi từ. – Alex

+0

Huh, phải @Alex. Tôi không chắc mình đã từng sử dụng điều đó chưa. Tốt đẹp! Cảm ơn. – Gray

3

Bất cứ khi nào chương trình của bạn có thể sử dụng một cách chính xác một trong hai (ThreadLocalhoặc biến địa phương), chọn các biến địa phương: nó sẽ được performant hơn.

ThreadLocal là để lưu trữ trạng thái theo luồng qua phạm vi thực hiện của phương thức. Rõ ràng các biến cục bộ không thể tồn tại trong phạm vi khai báo của chúng. Nếu bạn cần chúng, đó là khi bạn có thể bắt đầu sử dụng ThreadLocal.

Tùy chọn khác đang sử dụng synchronized để quản lý quyền truy cập vào biến thành viên được chia sẻ. Đây là một chủ đề phức tạp và tôi sẽ không bận tâm đến nó ở đây vì nó đã được giải thích và ghi chép bởi nhiều người nói rõ hơn tôi ở những nơi khác. Rõ ràng đây không phải là một biến thể của bộ nhớ "cục bộ" - bạn sẽ chia sẻ quyền truy cập vào một tài nguyên duy nhất theo cách an toàn.

+0

Tôi chưa bao giờ hiểu lưu trữ ThreadLocal. Tôi đã viết các ứng dụng đa luồng trong 30 năm và chưa bao giờ sử dụng TLS. Khi một luồng được bắt đầu, lệnh CreateThread() hoặc bất kỳ cuộc gọi nào sẽ lấy địa chỉ khi nào chuỗi sẽ thực thi. Trong hầu hết các ngôn ngữ, đây là một thủ tục/hàm và một biến ngăn xếp cục bộ được khai báo sẽ có thời gian tồn tại của luồng. Trong các ngôn ngữ OO, thường có một tham số bổ sung trong cuộc gọi tạo có thể truyền 'this' và do đó một thread có thể có các thành viên dữ liệu cá thể. SO, tại sao TLS? Nó cho tôi cái gì 'đặc biệt'? –

1

Câu hỏi này được trả lời bởi quy tắc đơn giản rằng một biến sẽ được khai báo trong phạm vi bao quanh nhỏ nhất có thể. A ThreadLocal là phạm vi kèm theo lớn nhất có thể, do đó bạn chỉ nên sử dụng nó cho dữ liệu cần thiết trên nhiều phạm vi từ vựng. Nếu nó có thể là một biến địa phương, nó nên được.

+0

Tôi nghĩ rằng họ nên được gọi là 'Chủ đề toàn cầu', sau đó không ai sẽ sử dụng chúng. Trong nhiều năm, tôi đọc các bài viết, blog, bài viết 'Không sử dụng các biến toàn cục'. Sau đó, ai đó quyết định rằng chủ đề có thể có một cái gì đó gọi là 'TLS' - yippee! globals một lần nữa, nhưng dưới một tên khác để họ ở lại mát mẻ. Có ổn không khi viết các chương trình đơn luồng với nhiều biến toàn cục, miễn là chúng được lưu trong TLS của luồng chính? IMHO, điều này TLS thingy là một tính năng ngớ ngẩn mà không có gì mới. –

+0

@MartinJames Tôi đồng ý hoàn toàn. Tôi đã sử dụng chúng khoảng hai lần trong 15 năm và hối hận mỗi lần. – EJP

+0

@MartinJames Tôi đồng ý rằng 'ThreadLocal's nên được sử dụng ít hoặc tốt nhất là không hề. –

1

Tôi cũng đã nhầm lẫn lý do tại sao tôi cần ThreadLocal khi tôi chỉ có thể sử dụng biến địa phương, vì cả hai đều duy trì trạng thái của họ bên trong một chuỗi. Nhưng sau rất nhiều tìm kiếm và thử nghiệm tôi thấy tại sao ThreadLocal lại cần thiết.

tôi thấy hai sử dụng cho đến nay -

giá trị cụ thể
  1. Saving chủ đề bên trong đối tượng chia sẻ cùng
  2. Alternative để đi qua các biến như thông số thông qua N-lớp mã

1:

Nếu bạn có hai chuỗi hoạt động trên cùng một đối tượng và cả hai chủ đề sửa đổi đối tượng này - sau đó cả hai chủ đề tiếp tục mất các sửa đổi của họ với nhau.

Để làm cho đối tượng này có hai trạng thái riêng biệt cho mỗi chuỗi, chúng tôi khai báo đối tượng này hoặc một phần của nó ThreadLocal.

Tất nhiên, ThreadLocal chỉ mang lại lợi ích ở đây vì cả hai luồng đều chia sẻ cùng một đối tượng. Nếu họ đang sử dụng các đối tượng khác nhau, không cần đối tượng là ThreadLocal.

2:

Lợi ích thứ hai của ThreadLocal, có vẻ là một tác dụng phụ của nó được thực hiện như thế nào.

Biến ThreadLocal có thể là .set() bằng một chuỗi và sau đó là .get() ở bất kỳ nơi nào khác. .get() sẽ truy xuất cùng một giá trị mà chủ đề này đã đặt ở bất kỳ nơi nào khác. Chúng ta sẽ cần một wrapper có sẵn trên toàn cầu để thực hiện một lệnh .get() và .set(), để thực sự viết mã xuống.

Khi chúng tôi thực hiện một threadLocalVar.set() - như thể nó được đặt bên trong một số "bản đồ" toàn cục, trong đó , chuỗi hiện tại là chủ đề hiện tại.

Như thể - someGlobalMap.put (Thread.currentThread(), threadLocalVar);

Vì vậy, mười lớp xuống, khi chúng tôi thực hiện threadLocalVar.get() - chúng tôi nhận được giá trị chủ đề này đã đặt mười lớp lên.

threadLocalVar = someGlobalMap.get (Thread.currentThread());

Vì vậy, hàm ở cấp thứ mười không phải lug xung quanh biến này làm tham số và có thể truy cập nó bằng hàm .get() mà không lo lắng về nó có phải là từ chuỗi phải không.

Cuối cùng, vì biến ThreadLocal là một bản sao cho mỗi chuỗi, tất nhiên, nó không cần đồng bộ hóa. Tôi hiểu lầm ThreadLocal trước đó như là một thay thế cho đồng bộ hóa, rằng nó không phải là. Nó chỉ là một tác dụng phụ của nó, mà chúng ta không cần phải đồng bộ hóa hoạt động của biến này ngay bây giờ.

Hy vọng điều này đã hữu ích.