2012-01-26 32 views
14

Tôi đang cố gắng sử dụng cả hai InheritableThreadLocal và một ThreadPoolExecutor.Sử dụng InheritableThreadLocal với ThreadPoolExecutor - hoặc - một ThreadPoolExecutor không sử dụng lại các chủ đề

Điều này bị hỏng do ThreadPoolExecutor sử dụng lại các chủ đề cho mỗi nhóm (sau cùng là hồ bơi), nghĩa là InheritableThreadLocal không hoạt động như mong đợi. Bây giờ vấn đề có vẻ hiển nhiên đối với tôi, nhưng nó đặc biệt khó hiểu để theo dõi.

Tôi sử dụng InheritableThreadLocal để mỗi một số quy trình cấp cao nhất có kết nối cơ sở dữ liệu riêng cho chính nó và mọi quy trình phụ mà nó sinh ra. Tôi không chỉ sử dụng một nhóm kết nối được chia sẻ vì mỗi quy trình cấp cao nhất sẽ thực hiện nhiều bước với kết nối của nó trước khi cam kết với cơ sở dữ liệu và/hoặc chuẩn bị rất nhiều PreparedStatements được sử dụng nhiều lần.

Tôi sử dụng số chia sẻ ThreadPoolExecutor giữa các quy trình cấp cao nhất này vì có một số hành vi nhất định cần được kiểm soát. ví dụ. Mặc dù tôi có thể có 4 quy trình cấp cao nhất đang chạy, tôi chỉ có thể có bất kỳ một quy trình nào bằng văn bản cho cơ sở dữ liệu tại một thời điểm (hoặc hệ thống cần phải truy cập vào một số tài nguyên được chia sẻ khác). Vì vậy, tôi sẽ có quy trình cấp cao nhất tạo một Runnable và gửi nó đến số ThreadPoolExecutor được chia sẻ để đảm bảo rằng không quá một (hoặc hai hoặc ba trường hợp có thể) đang chạy cùng một lúc trên toàn bộ hệ thống.

Vấn đề là do ThreadPoolExecutor sử dụng lại chủ đề của nó cho các hồ bơi, InheritableThreadLocal đang chọn giá trị ban đầu được chạy trong nhóm đó thay vì giá trị trong quy trình cấp cao nhất đã gửi Runnable đến ThreadPoolExecutor.

  • Có cách nào để buộc các hồ bơi công nhân trong ThreadPoolExecutor sử dụng giá trị InheritableThreadLocal đó là trong bối cảnh của quá trình đó tạo ra Runnable hơn là trong bối cảnh tái sử dụng hồ bơi thread?

  • Ngoài ra, có việc triển khai ThreadPoolExecutor nào tạo chủ đề mới mỗi khi khởi động Runnable mới không? Đối với mục đích của tôi, tôi chỉ quan tâm đến việc gating số lượng các chuỗi chạy đồng thời với một kích thước cố định.

  • Có giải pháp hoặc đề xuất nào khác mà mọi người có để tôi thực hiện những gì tôi đã mô tả ở trên không?

(Trong khi tôi nhận ra tôi có thể giải quyết vấn đề bằng cách thông qua xung quanh việc kết nối cơ sở dữ liệu từ lớp học để subthread để subthread như một số loại xe đạp cộng đồng, tôi muốn tránh điều này.)

Có một câu hỏi trước trên StackOverflow, InheritableThreadLocal and thread pools, cũng giải quyết vấn đề này. Tuy nhiên, giải pháp cho vấn đề đó dường như là một trường hợp sử dụng kém cho InheritableThreadLocal, mà tôi không nghĩ là áp dụng cho tình huống của tôi.

Cảm ơn mọi ý tưởng.

Trả lời

5

Thay vì sử dụng ThreadPoolExecutor để bảo vệ tài nguyên được chia sẻ, tại sao không sử dụng java.util.concurrent.Semaphore? Các nhiệm vụ phụ bạn tạo sẽ chạy để hoàn thành trong các chủ đề của riêng họ, nhưng chỉ sau khi đã có được giấy phép từ semaphore, và tất nhiên việc phát hành giấy phép khi thực hiện xong.

+0

Tôi đã không sử dụng Semaphores vì ​​vậy tôi sẽ xem xét điều này. Chúng tôi sử dụng rất nhiều tính năng khác của Executors (Futures, timeouts,…) nhưng tôi có thể làm điều đó với Semaphores hoặc tôi có thể sử dụng một người thực hiện thường xuyên (không phải thread pool) cùng với semaphores để có được mọi thứ tôi cần. Cảm ơn. –

+0

Đây là cách đi đúng đắn. Sử dụng Semaphores và FutureTasks là một mô hình đại diện hơn cho những gì chúng tôi muốn làm. Sau khi thay đổi mã đã được đơn giản hóa rất nhiều, đó luôn luôn là một dấu hiệu tốt. Plus nó giải quyết bất kỳ vấn đề InheritableThreadLocal. –

0

Chúng tôi đã có cùng một vấn đề trước đó và chúng tôi giải quyết vấn đề này bằng cách viết ThreadLocalContextMigrator mà về cơ bản sao chép bối cảnh cục bộ chủ đề vào nhiệm vụ sẽ được thực hiện bằng cách sử dụng một chuỗi từ hồ bơi. Nhiệm vụ, trong khi thực hiện sẽ thu thập thêm thông tin ngữ cảnh và sau khi hoàn thành nhiệm vụ, chúng tôi sao chép nó trở lại.

+0

Bạn có thể cung cấp thêm một chút chi tiết về cách bạn đã thực hiện việc này không? –

+0

'ThreadLocalContextMigrator.java' có ở đâu đó công khai không? –

3

sử dụng InheritedThreadLocal gần như chắc chắn là sai. Có lẽ bạn đã không hỏi câu hỏi nếu bạn có thể phù hợp với công cụ kỳ lạ đó. Đầu tiên và trước hết nó dễ bị rò rỉ khủng khiếp và thường là (các) giá trị thoát ra trong một số chủ đề hoàn toàn lạ.

Đối với Runnable là liên kết w/ngữ cảnh. Ghi đè công khai void execute(Runnable command) của ExecutorPool và quấn Runnable với một số ngữ cảnh mang giá trị bạn muốn ở vị trí đầu tiên từ số InheritedThreadLocal.

Các gói lớp sẽ giống như thế

class WrappedRunnable extends Runnable{ 
    static final ThreadLocal<Ctx> context=new ThreadLocal<Ctx>(); 
    final Runnable target; 
    final Ctx context; 
    WrappedRunnable(Ctx context, Runnable target){...} 

    public void run(){ 
    ctx.set(context); 
    try{ 
     target.run(); 
    }finally{ 
     ctx.set(null);//or ctx.remove() 
    } 
    } 
} 

Ngoài ra, là có bất kỳ thực hiện ThreadPoolExecutor tạo ra một mới> sợi mỗi lần nó bắt đầu một Runnable mới? Vì mục đích của tôi, tôi chỉ quan tâm đến việc gating số lượng các chuỗi chạy đồng thời với một kích thước cố định.

Trong khi thực sự xấu từ thời điểm thực hiện xem, bạn có thể thực hiện riêng của bạn, về cơ bản bạn chỉ cần execute(Runnable task) phương pháp cho chúng sinh sản Executor chủ đề mới và bắt đầu nó.

+0

Điều gì khiến bạn nói rằng InheritableThreadLocal có thể bị rò rỉ? Tôi là Googling về nó và không nhìn thấy bất kỳ bài viết hoặc ví dụ về nơi nó gây ra rò rỉ bộ nhớ. Bạn có thể chỉ cho tôi một số nguồn không? –

+0

@ JeffGoldberg, vâng, tôi biết rất rõ cách nó được triển khai và tôi làm rất nhiều cơ sở hạ tầng/phần mềm trung gian. ThreadLocal là dễ bị rò rỉ và phiên bản kế thừa là rất dễ bị rò rỉ. Hầu như bạn không thể kiểm soát khi một chủ đề mới được sinh ra vì lý do gì (JDK chính nó làm điều đó). Một chuỗi dài chạy sẽ làm rò rỉ giá trị và trình nạp lớp của nó và không có cách nào để ngăn chặn luồng xa xa đó khỏi việc giữ tham chiếu đến đối tượng vô dụng khác. 'childValue' có thể sửa đổi trường hợp nhưng nó được gọi trong một chủ đề giữ nó, và luồng đích mới không rõ ... – bestsss

+0

Một lưu ý nhanh: Cách tiếp cận này đã làm việc. Tôi đã kết thúc lấy lời khuyên từ @ bốn mươi hai và thay đổi cách tiếp cận thay vào đó, nếu không tôi sẽ chọn điều này làm câu trả lời. Cảm ơn lời khuyên và giúp đỡ. –

0

Tại sao không chỉ chuyển kết nối hiện tại vào bất kỳ tác vụ phụ nào được tạo ra bởi tác vụ chính? có thể một số loại đối tượng bối cảnh được chia sẻ?

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