Lý do gọi shutdown () trên ExecutorService


84

Tôi đã đọc về nó khá một chút trong vài giờ qua, và tôi chỉ đơn giản là không thể nhìn thấy bất cứ lý do ( có giá trị lý do) để gọi shutdown()về ExecutorService, trừ khi chúng ta có một ứng dụng khổng lồ mà các cửa hàng, hàng chục và hàng chục dịch vụ thi hành di chúc khác nhau mà không được sử dụng cho một thời gian dài.

Điều duy nhất (từ những gì tôi thu thập được) việc tắt máy làm được, là làm những gì một Thread bình thường làm sau khi nó hoàn thành. Khi Thread bình thường sẽ kết thúc phương thức chạy của Runnable (hoặc Callable), nó sẽ được chuyển đến Garbage Collection để được thu thập. Với Executor Service, các chủ đề sẽ đơn giản được tạm dừng, chúng sẽ không được đánh dấu để thu gom rác. Vì vậy, việc tắt máy là cần thiết.

Ok trở lại câu hỏi của tôi. Có lý do gì để gọi tắt máy ExecutorServicerất thường xuyên, hoặc thậm chí ngay sau khi thực hiện một số tác vụ? Tôi muốn để lại trường hợp ai đó đang làm điều đó và ngay sau đó gọi đến awaitTermination()vì điều này đã được xác thực. Một khi chúng tôi làm điều đó, chúng tôi phải tạo lại một ExecutorServicelần nữa một cái mới , để làm điều tương tự. Không phải là toàn bộ ý tưởng ExecutorServiceđể sử dụng lại các chủ đề? Vậy tại sao lại phá hủy ExecutorServicesớm như vậy?

Nó không phải là một cách hợp lý để chỉ cần tạo ExecutorService(hoặc một số tùy thuộc vào số lượng bạn cần), sau đó trong quá trình ứng dụng đang chạy, chuyển cho chúng các tác vụ khi chúng xuất hiện và sau đó khi thoát ứng dụng hoặc một số giai đoạn quan trọng khác, hãy tắt các trình thực thi đó ?

Tôi muốn câu trả lời từ một số lập trình viên có kinh nghiệm, những người viết nhiều mã không đồng bộ bằng ExecutorServices.

Câu hỏi thứ hai, giao dịch nhỏ hơn một chút với nền tảng Android. NẾU một số bạn sẽ nói rằng không phải ý tưởng tốt nhất là tắt máy thực thi mọi lúc, và bạn lập trình trên android, bạn có thể cho tôi biết cách bạn xử lý những lần tắt máy đó không (cụ thể là khi nào bạn thực thi chúng) khi chúng tôi xử lý các sự kiện vòng đời ứng dụng.

Vì nhận xét CommonsWare tôi đã đưa ra bài viết trung lập. Tôi thực sự không quan tâm đến việc tranh cãi về nó đến chết và có vẻ như nó đang dẫn đến đó. Tôi chỉ muốn tìm hiểu về những gì tôi đã hỏi ở đây từ các nhà phát triển có kinh nghiệm, nếu họ sẵn sàng chia sẻ kinh nghiệm của mình. Cảm ơn.


3
"Tôi thấy nhiều lần các mã mẫu, trong đó luôn có lệnh gọi shutdown () ngay sau khi phục tùng hoặc thực thi các tác vụ" - hãy sử dụng các siêu liên kết để cung cấp bằng chứng về tuyên bố của bạn. Cá nhân tôi chưa bao giờ thấy bất kỳ "mã mẫu" nào làm những gì bạn nêu. Có thể bạn đang hiểu sai điều gì đó và chúng tôi chỉ có thể chỉ ra điều đó cho bạn nếu chúng tôi biết bạn đang kiểm tra "mã mẫu" nào.
CommonsWare

4
Xin chào CommonsWare. Trước hết, tôi thấy một giọng điệu nghiêm khắc của bạn (hoặc có vẻ như vậy) đối với tôi, điều mà tôi nghĩ không được xác thực ở đây. Tôi không cố gắng vẽ chân dung mọi người theo cách tiêu cực. Về phần trích dẫn của bạn, tôi chủ yếu nói về phần Tư duy trong Java IV, Đa nhiệm. Bạn có thể tìm thấy nhiều trường hợp của điều đó trong các ví dụ của Bruce Eckel. Chúng hầu hết đều đơn giản, nhưng không bao giờ ít ấn tượng mà Bruce gây ra cho tôi, đó là việc tắt máy rất thường xuyên. Theo bất kỳ cách nào, bạn đã tập trung vào điều gì đó không phải là phần chính của bài đăng của tôi. Tôi đã loại bỏ những phần đó vì tôi thực sự không muốn tranh luận về nó.
Lucas

1
hay @CommonsWare in Thinking in java book của Bruce Eckel..trong đồng thời / Người thực hiện trang 804 Phiên bản thứ tư, anh ấy luôn sử dụng phương thức shutdown () ngay sau khi gửi hoặc thực thi các tác vụ trong các ứng dụng đơn giản để minh họa cách Người thực thi hoạt động như Lucas đã nói
Lỗi

2
Tôi biết đây là một bài viết cũ, nhưng tôi nghĩ câu hỏi của OP vẫn còn nguyên giá trị. Tôi cũng đã xem qua nhiều mã mẫu trong đó "có lệnh gọi shutdown () ngay sau khi thực thi ()". tutorial.jenkov.com/java-util-concurrent/execarieservice.html ( hướng dẫn đầu tiên xuất hiện khi bạn google "ví dụ về java
executiveervice

Cảm ơn bạn, tôi đã có cùng một câu hỏi được nêu ra với những "mã mẫu". journaldev.com/2340/…
Gregordy

Câu trả lời:


57

Các shutdown()phương pháp thực hiện một điều: ngăn chặn khách hàng để gửi làm việc nhiều hơn với dịch vụ thi hành di chúc. Điều này có nghĩa là tất cả các tác vụ hiện có sẽ vẫn chạy để hoàn thành trừ khi các hành động khác được thực hiện. Điều này đúng ngay cả đối với các tác vụ đã lên lịch, ví dụ: đối với một Dịch vụ được lập lịch trình: các phiên bản mới của tác vụ đã lên lịch sẽ không chạy. Điều này có thể hữu ích trong các tình huống khác nhau.

Giả sử bạn có một ứng dụng bảng điều khiển có một dịch vụ thực thi đang chạy N tác vụ. Nếu người dùng nhấn CTRL-C, bạn mong đợi ứng dụng sẽ kết thúc, có thể là một cách duyên dáng. Nó có nghĩa là gì? Có thể bạn muốn ứng dụng của mình không thể gửi thêm tác vụ đến dịch vụ người thực thi và đồng thời bạn muốn đợi N nhiệm vụ hiện có của mình hoàn thành. Bạn có thể đạt được điều này bằng cách sử dụng móc tắt máy như một phương sách cuối cùng:

final ExecutorService service = ... // get it somewhere

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Performing some shutdown cleanup...");
        service.shutdown();
        while (true) {
            try {
                System.out.println("Waiting for the service to terminate...");
                if (service.awaitTermination(5, TimeUnit.SECONDS)) {
                    break;
                }
            } catch (InterruptedException e) {
            }
        }
        System.out.println("Done cleaning");
    }
}));

Hook này sẽ tắt dịch vụ, điều này sẽ ngăn ứng dụng của bạn gửi các tác vụ mới và đợi tất cả các tác vụ hiện có hoàn thành trước khi tắt JVM. Việc chấm dứt đang chờ sẽ bị chặn trong 5 giây và trả về true nếu dịch vụ bị tắt. Điều này được thực hiện theo vòng lặp để bạn chắc chắn rằng cuối cùng dịch vụ sẽ tắt. InterruptException bị nuốt mỗi lần. Đây là cách tốt nhất để tắt một dịch vụ thực thi được sử dụng lại trên toàn bộ ứng dụng của bạn.

Mã này không hoàn hảo. Trừ khi bạn hoàn toàn tích cực, các nhiệm vụ của bạn cuối cùng sẽ kết thúc, bạn có thể muốn đợi một khoảng thời gian nhất định và sau đó chỉ cần thoát ra, bỏ qua các chuỗi đang chạy. Trong trường hợp này, bạn cũng nên gọi shutdownNow()sau thời gian chờ trong nỗ lực cuối cùng để làm gián đoạn các luồng đang chạy ( shutdownNow()cũng sẽ cung cấp cho bạn danh sách các tác vụ đang chờ chạy). Nếu nhiệm vụ của bạn được thiết kế để đáp ứng với sự gián đoạn, điều này sẽ hoạt động tốt.

Một tình huống thú vị khác là khi bạn có một SchedisedExecutorService thực hiện một nhiệm vụ định kỳ. Cách duy nhất để dừng chuỗi nhiệm vụ định kỳ là gọi điện shutdown().

CHỈNH SỬA: Tôi muốn nói thêm rằng tôi không khuyên bạn nên sử dụng móc tắt như được hiển thị ở trên trong trường hợp chung: nó có thể dễ xảy ra lỗi và chỉ nên là phương án cuối cùng. Hơn nữa, nếu bạn đã đăng ký nhiều hook tắt máy, thứ tự mà chúng sẽ chạy là không xác định, điều này có thể không mong muốn. Tôi muốn có ứng dụng một cách rõ ràng gọi shutdown()trên InterruptedException.


Xin lỗi Giovanni vì đã trả lời muộn, và cảm ơn bạn vì btw. Có, tôi biết về cách Executor hoạt động, điều này tôi đã cố gắng giải thích trong câu hỏi của mình. Việc tắt máy thực hiện những gì bạn đã nói, và nó cũng cho phép bộ thu gom rác thu thập các chuỗi chết đó và thực tế là thu thập cá thể ExecutorService. Câu hỏi của tôi là cụ thể. Có lý do gì để gọi "shutdown ()" mọi lúc, ngay sau khi bạn gửi / thực thi bất kỳ thứ gì trên ExecutorService. Phần thứ hai của câu hỏi hoàn toàn liên quan đến kiến ​​trúc Android. Nếu câu trả lời cho phần trước là không, thì khi nào gọi tắt trong và. vòng đời.
Lucas

3
Không có lý do gì để gọi tắt máy () mọi lúc. Trên thực tế, đó có thể là điều hoàn toàn sai vì nó sẽ ngăn bạn sử dụng lại dịch vụ của người thực thi một lần nữa. Lý do gọi nó ở cuối vòng đời của dịch vụ là để cuối cùng các luồng có thể được thu gom như bạn đã nhận thấy. Nếu bạn không làm như vậy, các luồng đó sẽ giữ cho JVM tồn tại ngay cả khi chúng không hoạt động.
Giovanni Botta

"Không có lý do gì để gọi shutdown () mọi lúc. Thực tế, đó có thể là điều hoàn toàn sai lầm vì nó sẽ ngăn bạn sử dụng lại dịch vụ thực thi một lần nữa". Đó chính xác là lý do của tôi, và sự bối rối của tôi từ câu hỏi ban đầu. Vì vậy, để nhắc lại, câu hỏi là: Khi nào tôi nên tắt ExecutiveService của mình trong vòng đời Android?
Lucas

2
Tôi không có kinh nghiệm Android, nhưng tôi đoán bạn nên tắt nó khi ứng dụng của bạn tắt, để cho phép JVM cuối cùng thoát.
Giovanni Botta

3
Tôi hiểu rồi. Tôi khuyên bạn nên sử dụng một nhóm luồng được lưu trong bộ nhớ cache và không bao giờ gọi shutdown()nó để không lãng phí tài nguyên khi không cần thiết. Nếu ứng dụng tắt, các luồng trong nhóm cuối cùng sẽ được thu gom rác (sau khi chúng không hoạt động trong 60 giây theo mặc định). Lưu ý rằng nếu bạn muốn nhóm bị ràng buộc hoặc muốn có thời gian tồn tại của luồng khác, bạn có thể tạo ThreadPoolExecutortrực tiếp.
Giovanni Botta

13

Không phải toàn bộ ý tưởng để ExecutorService sử dụng lại các chuỗi sao? Vậy tại sao lại phá hủy ExecutorService sớm như vậy?

Đúng. Bạn không nên phá hủy và tạo lại ExecutorServicethường xuyên. Khởi tạo ExecutorServicekhi bạn yêu cầu (chủ yếu là khi khởi động) và giữ cho nó hoạt động cho đến khi bạn làm xong.

Đó không phải là một cách hợp lý khi chỉ cần tạo ExecutorService (hoặc một số tùy thuộc vào số lượng bạn cần), sau đó trong quá trình ứng dụng đang chạy, chuyển cho chúng các tác vụ khi chúng xuất hiện và sau đó khi thoát ứng dụng hoặc một số giai đoạn quan trọng khác, hãy tắt chúng những người thừa hành?

Đúng. Thật hợp lý khi tắt máy ExecutorServiceở các giai đoạn quan trọng như thoát ứng dụng, v.v.

Câu hỏi thứ hai, giao dịch nhỏ hơn một chút với nền tảng Android. NẾU một số bạn nói rằng không phải ý tưởng tốt nhất là tắt các trình thực thi mỗi lần và bạn lập trình trên android, bạn có thể cho tôi biết cách bạn xử lý những lần tắt đó không (cụ thể là khi nào bạn thực thi chúng) khi chúng tôi xử lý các sự kiện khác nhau của ứng dụng vòng đời.

Giả sử điều đó ExecutorServiceđược chia sẻ trên các Hoạt động khác nhau trong ứng dụng của bạn. Mỗi hoạt động sẽ bị tạm dừng / tiếp tục vào các khoảng thời gian khác nhau và bạn vẫn cần một hoạt động ExecutorServicecho mỗi ứng dụng của mình.

Thay vì quản lý trạng thái ExecutorServicetrong các phương thức vòng đời Hoạt động, hãy chuyển quản lý ExecutorService (Tạo / Tắt máy) sang Dịch vụ tùy chỉnh của bạn .

Tạo ExecutorServicetrong Dịch vụ => onCreate()và tắt nó đúng cách trongonDestroy()

Cách tắt được đề xuất ExecutorService:

Cách tắt java ExecutorService đúng cách


3

Một ExecutorService sẽ được tắt khi nó không còn cần thiết để giải phóng tài nguyên hệ thống và cho phép tắt ứng dụng một cách dễ dàng. Bởi vì các luồng trong ExecutorService có thể là các luồng nondaemon, chúng có thể ngăn chặn việc chấm dứt ứng dụng bình thường. Nói cách khác, ứng dụng của bạn vẫn chạy sau khi hoàn thành phương thức chính của nó.

Sách tham khảo

Chaper: 14 Trang: 814


0

Lý do gọi shutdown () trên ExecutorService

Hôm nay tôi gặp phải một tình huống là tôi phải đợi cho đến khi một máy sẵn sàng, trước khi bắt đầu một loạt các tác vụ trên máy đó.

Tôi thực hiện cuộc gọi REST tới máy này, nếu tôi không nhận được 503 (Máy chủ không khả dụng) thì máy đã sẵn sàng xử lý các yêu cầu của tôi. Vì vậy, tôi đợi cho đến khi tôi đạt được 200 (Thành công) cho cuộc gọi REST đầu tiên.

Có nhiều cách để đạt được nó, tôi đã sử dụng ExecutorService để tạo một luồng và lên lịch nó chạy sau mỗi X Giây. Vì vậy, tôi cần dừng chuỗi này với một điều kiện, hãy kiểm tra điều này ...

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    Runnable task = () -> {
        try {
            int statusCode = restHelper.firstRESTCall();

            if (statusCode == 200) {
                executor.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    int retryAfter = 60;
    executor.scheduleAtFixedRate(task, 0, retryAfter, TimeUnit.SECONDS);

Câu hỏi thứ hai, giao dịch nhỏ hơn một chút với nền tảng Android.

Có lẽ tôi có thể trả lời nếu bạn cung cấp thêm ngữ cảnh! Cũng theo kinh nghiệm của tôi với việc phát triển Android, hiếm khi bạn cần Threads. Bạn đang phát triển một Trò chơi hoặc một ứng dụng cần các chuỗi để đạt hiệu suất? Nếu không, trong Android, bạn có các cách khác để giải quyết các vấn đề như tình huống mà tôi đã giải thích ở trên. Bạn có thể sử dụng TimerTask, AsyncTask hoặc Handlers hoặc Loaders dựa trên ngữ cảnh. Điều này là do nếu UIThread đợi lâu, bạn sẽ biết điều gì xảy ra: /


0

Điều này là chính xác, mặc dù đối với các cam kết đã lên kế hoạch, ví dụ: đối với Dịch vụ được lập kế hoạch: các trường hợp mới của nhiệm vụ đã đặt trước sẽ không chạy.

Chúng tôi sẽ mong đợi bạn có một ứng dụng thoải mái có quản trị đại lý chạy N việc vặt.

Tôi không nắm bắt nó có nghĩa là dễ dàng? Có lẽ bạn cần đơn đăng ký của mình để không có tùy chọn nộp thêm các nhiệm vụ cho ban quản trị đại lý và trong thời gian chờ đợi, bạn cần phải chờ đợi N cam kết hiện tại của bạn hoàn thành.

Ngoại trừ nếu bạn hoàn toàn tích cực thì cuối cùng công việc lặt vặt của bạn sẽ làm, bạn nên ngồi yên trong một khoảng thời gian nhất định và sau đó chỉ cần thoát ra, bỏ qua các chuỗi đang chạy.

Trong trường hợp các hoạt động của bạn nhằm phản ứng với sự can thiệp, điều này sẽ hoạt động tốt.

Một tình huống hấp dẫn khác là tại thời điểm bạn có SchedisedExecutorService thực hiện một hoạt động.

Cách tốt nhất để dừng chuỗi hoạt động là gọi shutdown ()

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.