Hẹn giờ Java vs ExecutorService?


263

Tôi có mã nơi tôi lên lịch một tác vụ bằng cách sử dụng java.util.Timer. Tôi đã nhìn xung quanh và thấy ExecutorServicecó thể làm như vậy. Vì vậy, câu hỏi này ở đây, bạn đã sử dụng TimerExecutorServiceđể lên lịch các nhiệm vụ, lợi ích của việc sử dụng này so với người khác là gì?

Cũng muốn kiểm tra xem có ai đã sử dụng Timerlớp này không và gặp phải bất kỳ vấn đề nào đã ExecutorServicegiải quyết cho họ.


1
Và nếu bạn cần một cái gì đó thậm chí nhiều tính năng hơn, hãy kiểm tra thạch anh . Nó cung cấp cho bạn rất nhiều kiểm soát công việc, bao gồm cả cron như lập lịch, lập lịch nhận biết cụm, kiểm soát cá nhân đối với các công việc (các khái niệm như chạy một lúc, phụ thuộc, v.v.). --Tim
Tim

Câu trả lời:


313

Theo Java đồng thời trong thực tiễn :

  • Timercó thể nhạy cảm với những thay đổi trong đồng hồ hệ thống, ScheduledThreadPoolExecutorkhông.
  • Timerchỉ có một luồng thực thi, vì vậy tác vụ chạy dài có thể trì hoãn các tác vụ khác. ScheduledThreadPoolExecutorcó thể được cấu hình với bất kỳ số lượng chủ đề. Hơn nữa, bạn có toàn quyền kiểm soát các chủ đề được tạo, nếu bạn muốn (bằng cách cung cấp ThreadFactory).
  • Các ngoại lệ thời gian chạy bị ném vào TimerTaskgiết một luồng đó, do đó làm Timerchết :-( ... tức là các tác vụ theo lịch trình sẽ không chạy nữa. ScheduledThreadExecutorKhông chỉ bắt ngoại lệ thời gian chạy, mà nó còn cho phép bạn xử lý chúng nếu bạn muốn (bằng cách ghi đè afterExecutephương thức từ ThreadPoolExecutor). ngoại lệ ném sẽ bị hủy, nhưng các nhiệm vụ khác sẽ tiếp tục chạy.

Nếu bạn có thể sử dụng ScheduledThreadExecutorthay vì Timer, hãy làm như vậy.

Một điều nữa ... trong khi ScheduledThreadExecutorkhông có sẵn trong thư viện Java 1.4, thì có một Backport của JSR 166 ( java.util.concurrent) cho Java 1.2, 1.3, 1.4 , có ScheduledThreadExecutorlớp.


63

Nếu nó có sẵn cho bạn, thì thật khó để nghĩ ra lý do không sử dụng khung thực thi Java 5. Gọi điện thoại:

ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();

sẽ cung cấp cho bạn một ScheduledExecutorServicechức năng tương tự Timer(nghĩa là nó sẽ đơn luồng) nhưng quyền truy cập của nó có thể mở rộng hơn một chút (dưới mui xe, nó sử dụng các cấu trúc đồng thời thay vì đồng bộ hóa hoàn toàn như với Timerlớp). Sử dụng một ScheduledExecutorServicecũng cung cấp cho bạn những lợi thế như:

  • Bạn có thể tùy chỉnh nó nếu cần (xem lớp newScheduledThreadPoolExecutor()hoặc ScheduledThreadPoolExecutorlớp)
  • Việc thực thi 'một lần' có thể trả về kết quả

Về những lý do duy nhất để gắn bó với Timertôi có thể nghĩ đến là:

  • Nó có sẵn Java 5
  • Một lớp tương tự được cung cấp trong J2ME, có thể giúp chuyển ứng dụng của bạn dễ dàng hơn (nhưng sẽ không quá khó để thêm một lớp trừu tượng phổ biến trong trường hợp này)

1
Một lý do khác để sử dụng TimerTaskcó thể là sự sẵn có của scheduledExecutionTime()phương pháp mà dường như không có bất kỳ tương đương nào ScheduledExecutorService.
Rohit Agarwal

3
Một lưu ý khác: Tôi đang viết bình luận này trong 2k17, không còn J2ME nữa. nó đã chết
mseach

1
Java Timer-class là crappy.
JohnyTex

26

ExecutorService mới hơn và tổng quát hơn. Đồng hồ bấm giờ chỉ là một chuỗi chạy định kỳ những thứ bạn đã lên lịch cho nó.

ExecutorService có thể là một nhóm luồng hoặc thậm chí trải rộng trên các hệ thống khác trong một cụm và thực hiện những việc như thực thi hàng loạt một lần, v.v ...

Chỉ cần nhìn vào những gì mỗi đề nghị để quyết định.



8

Từ trang tài liệu của Oracle trên lên lịchThreadPoolExecutor

Một ThreadPoolExecutor có thể lên lịch các lệnh để chạy sau một độ trễ nhất định hoặc để thực hiện định kỳ. Lớp này thích hợp hơn với Timer khi cần nhiều luồng công nhân hoặc khi cần thêm tính linh hoạt hoặc khả năng của ThreadPoolExecutor (mà lớp này mở rộng).

ExecutorService/ThreadPoolExecutorhoặc ScheduledThreadPoolExecutorlà sự lựa chọn rõ ràng khi bạn có nhiều luồng công nhân.

Ưu điểm của ExecutorServicehơnTimer

  1. Timerkhông thể tận dụng các lõi CPU có sẵn không giống như ExecutorServicenhiều tác vụ sử dụng các hương vị ExecutorServicenhư ForkJoinPool
  2. ExecutorServicecung cấp API hợp tác nếu bạn cần phối hợp giữa nhiều tác vụ. Giả sử rằng bạn phải gửi N số nhiệm vụ công nhân và chờ hoàn thành tất cả chúng. Bạn có thể dễ dàng đạt được nó với API invoke ALL . Nếu bạn muốn đạt được điều tương tự với nhiều Timernhiệm vụ, nó sẽ không đơn giản.
  3. ThreadPoolExecutor cung cấp API tốt hơn để quản lý vòng đời của Thread.

    Nhóm luồng xử lý hai vấn đề khác nhau: chúng thường cung cấp hiệu suất được cải thiện khi thực hiện số lượng lớn các tác vụ không đồng bộ, do giảm chi phí gọi cho mỗi tác vụ và chúng cung cấp một phương tiện ràng buộc và quản lý tài nguyên, bao gồm các luồng, được sử dụng khi thực hiện một bộ sưu tập nhiệm vụ. Mỗi ThreadPoolExecutor cũng duy trì một số thống kê cơ bản, chẳng hạn như số lượng tác vụ đã hoàn thành

    Vài ưu điểm:

    a. Bạn có thể tạo / quản lý / kiểm soát vòng đời của Chủ đề & tối ưu hóa chi phí tạo chủ đề

    b. Bạn có thể kiểm soát xử lý các tác vụ (Đánh cắp công việc, ForkJoinPool, invoke ALL), v.v.

    c. Bạn có thể theo dõi tiến trình và sức khỏe của chủ đề

    d. Cung cấp cơ chế xử lý ngoại lệ tốt hơn


5

Lý do của tôi đôi khi thích Timer hơn Executors.newSingleThreadSchediatedExecutor () là tôi nhận được mã sạch hơn nhiều khi tôi cần bộ hẹn giờ để thực thi trên các luồng daemon.

đối chiếu

private final ThreadFactory threadFactory = new ThreadFactory() {
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
};
private final ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(threadFactory); 

với

private final Timer timer = new Timer(true);

Tôi làm điều này khi tôi không cần sự mạnh mẽ của một dịch vụ điều hành.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.