2010-10-21 44 views

Trả lời

38

ExecutorService tóm tắt nhiều sự phức tạp liên quan đến các tóm tắt cấp thấp hơn như raw Thread. Nó cung cấp các cơ chế để bắt đầu, đóng, gửi, thực hiện và chặn một cách an toàn khi chấm dứt thành công hoặc đột ngột các tác vụ (được biểu thị là Runnable hoặc Callable).

Từ JCiP Mục 6.2, trực tiếp từ miệng của ngựa:

Executor có thể là một giao diện đơn giản, nhưng nó là cơ sở cho một khuôn khổ linh hoạt và mạnh mẽ để thực hiện nhiệm vụ không đồng bộ có hỗ trợ một loạt các nhiệm vụ chính sách thực thi. Nó cung cấp một phương tiện tiêu chuẩn để tách gửi nhiệm vụ từ thực hiện tác vụ, mô tả các tác vụ là Runnable. Việc triển khai Executor cũng cung cấp hỗ trợ vòng đời và móc để thêm thu thập số liệu thống kê, quản lý ứng dụng và theo dõi. ... Sử dụng Executor thường là con đường dễ nhất để thực hiện thiết kế của người tiêu dùng sản xuất trong ứng dụng của bạn.

Thay vì dành thời gian thực hiện (thường không chính xác và nỗ lực lớn) cơ sở hạ tầng cơ sở cho song song, khung j.u.concurrent cho phép bạn tập trung vào cấu trúc nhiệm vụ, phụ thuộc, song song tiềm năng. Đối với một loạt các ứng dụng đồng thời, việc xác định và khai thác các ranh giới nhiệm vụ là rất đơn giản và sử dụng j.u.c, cho phép bạn tập trung vào tập hợp con nhỏ hơn nhiều các thách thức đồng thời thực sự có thể yêu cầu các giải pháp chuyên biệt hơn.

Ngoài ra, mặc dù giao diện soạn sẵn và cảm nhận, các Oracle API page summarizing the concurrency utilities bao gồm một số đối số thực sự vững chắc cho việc sử dụng họ, không kém phần quan:

phát triển có nhiều khả năng đã hiểu các thư viện chuẩn lớp học, vì vậy không có cần phải tìm hiểu API và hành vi của các thành phần đồng thời . Ngoài ra, các ứng dụng đồng thời còn cách xa đơn giản hơn để gỡ lỗi khi chúng được xây dựng trên các thành phần đáng tin cậy, được thử nghiệm tốt.

question on SO hỏi về một cuốn sách hay, câu trả lời ngay lập tức là JCiP. Nếu bạn chưa có, hãy lấy cho mình một bản sao. Cách tiếp cận toàn diện để đồng thời trình bày ở đó cũng vượt ra ngoài câu hỏi này, và sẽ giúp bạn tiết kiệm rất nhiều đau khổ trong thời gian dài.

18

Một lợi thế mà tôi thấy là trong việc quản lý/lên lịch một số chuỗi. Với ExecutorService, bạn không cần phải viết trình quản lý luồng của riêng mình có thể bị lỗi với các lỗi. Điều này đặc biệt hữu ích nếu chương trình của bạn cần chạy nhiều luồng cùng một lúc. Ví dụ, bạn muốn thực hiện hai luồng cùng một lúc, bạn có thể dễ dàng làm điều đó như thế này:

ExecutorService exec = Executors.newFixedThreadPool(2); 

exec.execute(new Runnable() { 
    public void run() { 
    System.out.println("Hello world"); 
    } 
}); 

exec.shutdown(); 

Ví dụ có thể tầm thường, nhưng cố gắng nghĩ rằng "hello world" dòng chứa một hoạt động nặng và bạn muốn hoạt động đó chạy trong một vài luồng cùng một lúc để cải thiện hiệu suất của chương trình.Đây chỉ là một ví dụ, vẫn còn nhiều trường hợp mà bạn muốn lên lịch hoặc chạy một số luồng và sử dụng ExecutorService làm trình quản lý luồng của bạn.

Để chạy một chuỗi duy nhất, tôi không thấy bất kỳ lợi thế rõ ràng nào khi sử dụng ExecutorService.

+0

Nó không phải là exec.execute (Runnable mới() ..? – devnull

+0

Hoặc là okay kể từ khi chủ đề thực hiện Runnable.Đối với trường hợp đơn giản, Runnable nên được đầy đủ – Manny

+1

Tôi thực sự không nghĩ rằng có bất kỳ điểm nào trong việc tạo ra một ' Chủ đề' khi tất cả những gì bạn cần là 'Runnable' ... bạn thậm chí không bắt đầu 'Thread', vì vậy nó chỉ thêm nhầm lẫn và hành lý không cần thiết. – ColinD

9

Để chạy tác vụ bằng cách triển khai giao diện Runnable, chúng tôi phải tạo đối tượng của Thread như new thread(RunnableObject).start(). Nhưng chúng tôi biết rằng việc tạo chuỗi có chi phí riêng và được lưu trữ trong bộ nhớ Stack và Heap. Nó rất tốn kém để tạo một đối tượng chuỗi chỉ để chạy tác vụ trong chuỗi riêng biệt.

Executors khuôn khổ (java.util.concurrent.Executor), được phát hành bởi Java 5 trong gói java.util.concurrent được sử dụng để chạy các đối tượng chuỗi Runnable mà không cần tạo đối tượng Thread.

"Khung Executor là khuôn khổ để chuẩn hóa yêu cầu, lập lịch biểu, thực thi và kiểm soát các tác vụ không đồng bộ theo một bộ chính sách thực thi".

9

Dưới đây là một số lợi ích:

  1. dịch vụ Executor quản lý chủ đề theo cách không đồng bộ
  2. Sử dụng callable để có được kết quả trở lại sau khi kết thúc bài.
  3. Quản lý phân bổ công việc vào chủ đề miễn phí và làm việc bán lại hoàn thành từ chủ đề cho phân công công việc mới tự động
  4. ngã ba - tham gia khuôn khổ cho xử lý song song
  5. thông tin liên lạc tốt hơn giữa các chủ đề
  6. invokeAll và invokeAny cho kiểm soát nhiều hơn để chạy bất kỳ hoặc tất cả các chủ đề cùng một lúc
  7. tắt máy cung cấp khả năng hoàn thành tất cả các công việc được chỉ định theo chủ đề
  8. Dịch vụ thực thi theo lịch trình cung cấp các phương pháp để tạo lặp lại các cuộc gọi chạy và các cuộc gọi Hy vọng nó sẽ giúp bạn
+1

Không phải là "Tương lai" ở điểm thứ hai thay vì có thể gọi được? Tương lai là từ nơi chúng ta có thể lấy kết quả/Giá trị sau khi hoàn thành chuỗi. – AmitG

+0

Có như ví dụ. Tương lai future = executorService.submit (có thể gọi); –

1

Trước java phiên bản 1.5, Chủ đề/Runnable được thiết kế cho hai dịch vụ riêng biệt

  1. Đơn vị công tác
  2. Thi hành mà đơn vị làm việc

ExecutorService tách riêng những hai dịch vụ bằng cách chỉ định Runnable/Callable làm đơn vị công việc và Người thực thi làm cơ chế thực hiện (với vòng đời) đơn vị công việc

1

ExecutorService cũng cho phép truy cập vào FutureTask sẽ trở lại lớp gọi điện thoại kết quả của một tác vụ nền sau khi hoàn thành. Trong trường hợp triển khai có thể gọi

public class TaskOne implements Callable<String> { 

@Override 
public String call() throws Exception { 
    String message = "Task One here. . ."; 
    return message; 
    } 
} 

public class TaskTwo implements Callable<String> { 

@Override 
public String call() throws Exception { 
    String message = "Task Two here . . . "; 
    return message; 
    } 
} 

// from the calling class 

ExecutorService service = Executors.newFixedThreadPool(2); 
    // set of Callable types 
    Set<Callable<String>>callables = new HashSet<Callable<String>>(); 
    // add tasks to Set 
    callables.add(new TaskOne()); 
    callables.add(new TaskTwo()); 
    // list of Future<String> types stores the result of invokeAll() 
    List<Future<String>>futures = service.invokeAll(callables); 
    // iterate through the list and print results from get(); 
    for(Future<String>future : futures) { 
     System.out.println(future.get()); 
    } 
5

Các giới hạn sau đây từ chủ đề truyền thống được khắc phục bằng khung công tác (được xây dựng trong khuôn khổ Thread Pool).

  • Quản lý tài nguyên kém tức là tiếp tục tạo tài nguyên mới cho mọi yêu cầu. Không có giới hạn để tạo tài nguyên.Sử dụng khung công tác Executor, chúng tôi có thể tái sử dụng các tài nguyên hiện có và đặt giới hạn về việc tạo tài nguyên.
  • Không mạnh mẽ: Nếu chúng tôi tiếp tục tạo chủ đề mới, chúng tôi sẽ nhận được StackOverflowException ngoại lệ do đó JVM của chúng tôi sẽ gặp sự cố.
  • Tạo thời gian trên không: Đối với mỗi yêu cầu, chúng tôi cần tạo tài nguyên mới. Để tạo tài nguyên mới là tốn thời gian. tức là tác vụ Tạo chuỗi>. Sử dụng khung công tác Executor, chúng ta có thể xây dựng trong Thread Pool.

Lợi ích của Chủ đề Pool

  • Sử dụng Chủ đề Pool giảm thời gian phản ứng bằng cách tránh tạo thread trong yêu cầu hoặc xử lý công việc.

  • Sử dụng Thread Pool cho phép bạn thay đổi chính sách thực thi của mình nếu cần. bạn có thể chuyển từ chuỗi đơn thành nhiều chuỗi chỉ bằng cách thay thế triển khai ExecutorService.

  • Chủ đề Bể bơi trong ứng dụng Java tăng tính ổn định của hệ thống bằng cách tạo một số lượng cấu hình luồng được quyết định dựa trên tải hệ thống và tài nguyên có sẵn.

  • Thread Pool giải phóng nhà phát triển ứng dụng khỏi công cụ quản lý luồng và cho phép tập trung vào logic nghiệp vụ.

Source

1

Có thực sự đắt tiền để tạo ra một chủ đề mới?

Là điểm chuẩn, tôi vừa tạo 60.000 chủ đề với Runnable s với các phương thức trống rỗng run(). Sau khi tạo ra mỗi thread, tôi gọi phương thức start(..) của nó ngay lập tức. Việc này mất khoảng 30 giây hoạt động CPU mạnh. Các thử nghiệm tương tự đã được thực hiện để đáp ứng với this question. Tóm tắt của những người là nếu các chủ đề không hoàn thành ngay lập tức, và một số lượng lớn các chủ đề tích cực tích lũy (một vài nghìn), sau đó sẽ có vấn đề: (1) mỗi thread có một ngăn xếp, vì vậy bạn sẽ hết bộ nhớ , (2) có thể có giới hạn về số lượng chủ đề cho mỗi quá trình mà hệ điều hành áp đặt, nhưng not necessarily, it seems. Vì vậy, theo tôi có thể thấy, nếu chúng ta đang nói về việc khởi chạy 10 chủ đề mỗi giây và tất cả đều kết thúc nhanh hơn so với những chủ đề mới bắt đầu và chúng tôi có thể đảm bảo rằng tỷ lệ này sẽ không bị vượt quá quá nhiều , sau đó ExecutorService không cung cấp bất kỳ lợi thế cụ thể nào về hiệu suất hiển thị hoặc độ ổn định. (Mặc dù nó vẫn có thể làm cho nó thuận tiện hơn hoặc dễ đọc hơn để thể hiện một số ý tưởng đồng thời nhất định trong mã.) Mặt khác, nếu bạn có thể lên lịch hàng trăm hoặc hàng nghìn tác vụ mỗi giây, cần thời gian để chạy, bạn có thể gặp phải các vấn đề lớn đi thẳng. Điều này có thể xảy ra bất ngờ, ví dụ: nếu bạn tạo các luồng để đáp ứng các yêu cầu tới một máy chủ, và có sự tăng đột biến về cường độ các yêu cầu mà máy chủ của bạn nhận được. Nhưng ví dụ: một chủ đề đáp ứng mọi sự kiện đầu vào của người dùng (nhấn phím, chuyển động của chuột) có vẻ hoàn toàn ổn, miễn là các tác vụ ngắn gọn.

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