Cách hết thời gian chờ một chuỗi


257

Tôi muốn chạy một chủ đề trong một khoảng thời gian cố định. Nếu nó không được hoàn thành trong thời gian đó, tôi muốn giết nó, ném một số ngoại lệ hoặc xử lý nó theo một cách nào đó. Nó được hoàn thiện bằng cách nào?

Một cách thực hiện như tôi đã tìm ra từ luồng này là sử dụng TimerTask bên trong phương thức run () của Thread.

Có giải pháp nào tốt hơn cho việc này không?

 
CHỈNH SỬA: Thêm tiền thưởng khi tôi cần câu trả lời rõ ràng hơn. Mã ExecutorService được cung cấp bên dưới không giải quyết được vấn đề của tôi. Tại sao tôi nên ngủ () sau khi thực thi (một số mã - tôi không có quyền xử lý đoạn mã này)? Nếu mã được hoàn thành và sleep () bị gián đoạn, làm thế nào đó có thể là timeOut?

Nhiệm vụ cần được thực hiện không nằm trong tầm kiểm soát của tôi. Nó có thể là bất kỳ đoạn mã nào. Vấn đề là đoạn mã này có thể chạy vào một vòng lặp vô hạn. Tôi không muốn điều đó xảy ra. Vì vậy, tôi chỉ muốn chạy tác vụ đó trong một chuỗi riêng biệt. Luồng mẹ phải đợi cho đến khi luồng đó kết thúc và cần biết trạng thái của tác vụ (tức là liệu nó đã hết thời gian chờ hay một số ngoại lệ xảy ra hoặc nếu nó thành công). Nếu nhiệm vụ đi vào một vòng lặp vô hạn, chuỗi mẹ của tôi tiếp tục chờ đợi vô thời hạn, đó không phải là một tình huống lý tưởng.


CHỈNH SỬA: Thêm tiền thưởng khi tôi cần câu trả lời rõ ràng hơn. mã ExecutorService được cung cấp bên dưới không giải quyết được vấn đề của tôi. Tại sao tôi nên ngủ () sau khi thực thi mã của mình? Nếu mã được hoàn thành và giấc ngủ () bị gián đoạn, làm thế nào đó có thể là timeOut?
java_geek

7
Đó sleep()chỉ là sơ khai để đại diện cho "nhiệm vụ chạy trong thời gian dài". Chỉ cần thay thế nó với nhiệm vụ thực sự của bạn;)
BalusC

1
... một "nhiệm vụ chạy trong thời gian dài" xảy ra để trả lời interrupt()các cuộc gọi trên chuỗi của nó ... không phải tất cả các cuộc gọi "chặn" đều làm như tôi đã cố gắng chỉ ra trong câu trả lời của mình. Các chi tiết cụ thể của nhiệm vụ mà bạn đang cố gắng hủy bỏ tạo ra sự khác biệt rất lớn trong cách tiếp cận nên được sử dụng. Thông tin thêm về nhiệm vụ sẽ hữu ích.
erickson

Nếu những câu trả lời này không giải quyết được vấn đề, thì tôi đoán sẽ có thêm chi tiết / mã sẽ giúp trả lời.
Elister

Những chủ đề mà bạn muốn giới hạn thời gian; họ đang thực hiện các cuộc gọi chặn hay họ đang ở trong vòng lặp nào đó mà bạn có thể dễ dàng kiểm tra một số biến số để xem đã đến lúc thoát chưa?
Scott Smith

Câu trả lời:


380

Trên thực tế chứ không phải sử dụng ExecutorServicethay vì Timer, đây là một SSCCE :

package com.stackoverflow.q2275443;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try {
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
        return "Ready!";
    }
}

Chơi một chút với timeoutđối số trong Future#get()phương thức, ví dụ: tăng nó lên 5 và bạn sẽ thấy rằng chuỗi kết thúc. Bạn có thể chặn thời gian chờ trong catch (TimeoutException e)khối.

Cập nhật: để làm rõ một sự hiểu lầm khái niệm, sleep()không cần thiết. Nó chỉ được sử dụng cho mục đích SSCCE / trình diễn. Chỉ cần thực hiện nhiệm vụ lâu dài của bạn ngay tại đó sleep(). Bên trong tác vụ đang chạy lâu dài của mình, bạn nên kiểm tra xem chuỗi không bị gián đoạn như sau:

while (!Thread.interrupted()) {
    // Do your long running task here.
}

24
Thay thế Thread.sleep(4000)bằng một số câu lệnh dài hạn khác và ví dụ này sẽ không hoạt động. Nói cách khác, ví dụ này sẽ chỉ hoạt động nếu Taskđược thiết kế để hiểu sự Thread.isInterrupted()thay đổi trạng thái.
yegor256,

@BalusC Tôi đã thử phương pháp này để cố gắng chấm dứt các chuỗi của mình, nhưng không thể làm cho nó hoạt động. Bạn có thể kiểm tra tại đây: stackoverflow.com/questions/35553420/…
syfantid

InterruptException được quản lý bởi future.cancel (true) được xử lý như thế nào?
bolei

1
n số người đã nhận xét về tên gói và đây là một +1 khác cho nó. Đó là một kỹ năng tuyệt vời để được thấm nhuần. Cảm ơn!
Ashwin Tumma

@BalusC Tôi có nghi ngờ, liệu Tương lai có thực thi đồng bộ không và nếu mất hơn một thời gian xác định trước thì nó sẽ bị chấm dứt. Nếu không, nó sẽ thực thi trong tương lai một lúc nào đó trong khi chúng tôi đang tính đến thời gian ... Cảm ơn bạn
Adeel Ahmad

49

Không có cách nào đáng tin cậy 100% để làm điều này cho bất kỳ tác vụ cũ nào. Nhiệm vụ phải được viết với khả năng này trong tâm trí.

Các thư viện Java lõi như ExecutorServicehủy các tác vụ không đồng bộ với interrupt()các lệnh gọi trên chuỗi công nhân. Vì vậy, ví dụ: nếu nhiệm vụ chứa một số loại vòng lặp, bạn nên kiểm tra trạng thái ngắt của nó trên mỗi lần lặp. Nếu nhiệm vụ đang thực hiện các hoạt động I / O, chúng cũng sẽ bị gián đoạn — và việc thiết lập có thể rất phức tạp. Trong mọi trường hợp, hãy nhớ rằng mã phải chủ động kiểm tra các ngắt; thiết lập một ngắt không nhất thiết phải làm bất cứ điều gì.

Tất nhiên, nếu nhiệm vụ của bạn là một số vòng lặp đơn giản, bạn chỉ có thể kiểm tra thời gian hiện tại ở mỗi lần lặp và bỏ qua khi hết thời gian chờ được chỉ định. Một luồng công nhân không cần thiết trong trường hợp đó.


Theo kinh nghiệm của tôi, mã duy nhất không phản ứng để bắt đầu bị gián đoạn là đang chặn trong mã gốc (chờ hệ điều hành).
Thorbjørn Ravn Andersen,

@ ThorbjørnRavnAndersen Tôi đồng ý, nhưng đó là rất nhiều mã. Quan điểm của tôi là không có cơ chế mục đích chung nào cho việc này; bạn phải hiểu chính sách gián đoạn của nhiệm vụ.
erickson,

@erickson, tôi đồng ý với bạn. Đối với câu trả lời chính, Phải có một chính sách hủy bỏ được xác định cho mỗi nhiệm vụ, nếu bạn muốn dừng nó theo cách đó. Hoặc luồng nên biết những gì nó phải làm khi nó bị gián đoạn. Rốt cuộc, việc gián đoạn và dừng bất kỳ luồng nào cũng chỉ là một yêu cầu mà luồng đích có thể chấp nhận hoặc từ chối, vì vậy tốt hơn hết bạn nên ghi nhớ điều này.
AKS

không thể thực thi dịch vụ chọn chạy tác vụ trên chuỗi gọi? cũng có thể thực thi dịch vụ có thể chọn thực hiện nhiệm vụ vào một lúc nào đó trong tương lai?
filthy_wizard

@ user1232726 execute()Phương thức của giao diện mẹ, Executorcó thể chạy một tác vụ trong chuỗi gọi. Không có câu lệnh nào tương tự cho các submit()phương thức của các trường hợp ExecutorServicetrả về đó Future. Hàm ý của dịch vụ là có các luồng công nhân phải được dọn dẹp khi tắt máy và các tác vụ được thực thi không đồng bộ. Điều đó nói rằng, không có gì trong hợp đồng nói rằng ExecutorServicebị cấm thực hiện các tác vụ trong chuỗi gửi; những đảm bảo đó đến từ các API triển khai, như Executorscác nhà máy.
erickson

13

Cân nhắc sử dụng một phiên bản của ExecutorService . Cả hai invokeAll()invokeAny()phương thức đều có sẵn với một timeouttham số.

Luồng hiện tại sẽ chặn cho đến khi phương thức hoàn tất (không chắc liệu điều này có mong muốn hay không) vì (các) tác vụ đã hoàn thành bình thường hoặc đã đến thời gian chờ. Bạn có thể kiểm tra Future(các) hàng trả lại để xác định điều gì đã xảy ra.


9

Giả sử mã luồng nằm ngoài tầm kiểm soát của bạn:

Từ tài liệu Java được đề cập ở trên:

Điều gì sẽ xảy ra nếu một luồng không phản hồi với Thread.interrupt?

Trong một số trường hợp, bạn có thể sử dụng các thủ thuật dành riêng cho ứng dụng. Ví dụ: nếu một luồng đang chờ trên một ổ cắm đã biết, bạn có thể đóng ổ cắm để khiến luồng đó quay trở lại ngay lập tức. Thật không may, thực sự không có bất kỳ kỹ thuật nào hoạt động nói chung. Cần lưu ý rằng trong tất cả các tình huống mà một chuỗi đang chờ không phản hồi với Thread.interrupt, nó cũng sẽ không phản hồi với Thread.stop. Những trường hợp như vậy bao gồm các cuộc tấn công cố ý từ chối dịch vụ và các hoạt động I / O mà thread.stop và thread.interrupt không hoạt động bình thường.

Kết luận:

Đảm bảo rằng tất cả các luồng có thể bị gián đoạn, nếu không, bạn cần có kiến ​​thức cụ thể về luồng - chẳng hạn như phải đặt cờ. Có thể bạn có thể yêu cầu nhiệm vụ được giao cho bạn cùng với mã cần thiết để dừng nó - xác định giao diện bằng một stop()phương thức. Bạn cũng có thể cảnh báo khi không thể dừng một nhiệm vụ.


8

BalusC cho biết:

Cập nhật: để làm rõ sự hiểu lầm về khái niệm, không bắt buộc phải có sleep (). Nó chỉ được sử dụng cho mục đích SSCCE / trình diễn. Chỉ cần làm nhiệm vụ chạy dài của bạn ngay tại đó thay vì ngủ ().

Nhưng nếu bạn thay thế Thread.sleep(4000);bằng for (int i = 0; i < 5E8; i++) {}thì nó không biên dịch, bởi vì vòng lặp trống không ném một InterruptedException.

Và để luồng có thể bị gián đoạn, nó cần phải ném một InterruptedException.

Đây dường như là một vấn đề nghiêm trọng đối với tôi. Tôi không thể biết cách điều chỉnh câu trả lời này để làm việc với một nhiệm vụ dài hạn nói chung.

Đã chỉnh sửa để thêm: Tôi đã lý luận điều này như một câu hỏi mới: [ làm gián đoạn một chuỗi sau thời gian cố định, nó có phải ném InterruptException không? ]


Cách tôi làm điều đó là để thêm một'throws Exception' trong public class <T> Cuộc gọi {} Phương pháp
Roberto Linares

5

Tôi nghĩ bạn nên xem xét các cơ chế xử lý đồng thời thích hợp (các luồng chạy thành các vòng lặp vô hạn nghe có vẻ không tốt, btw). Hãy chắc chắn rằng bạn đã đọc một chút về chủ đề Chủ đề "giết" hoặc "dừng" .

Những gì bạn đang mô tả, nghe rất giống một "điểm hẹn", vì vậy bạn có thể muốn xem qua CyclicBarrier .

Có thể có các cấu trúc khác (như sử dụng CountDownLatch chẳng hạn) có thể giải quyết vấn đề của bạn (một chuỗi đang đợi với thời gian chờ chốt, cấu trúc kia sẽ đếm ngược chốt nếu nó đã hoạt động, điều này sẽ giải phóng chuỗi đầu tiên của bạn sau đó thời gian chờ hoặc khi bộ đếm ngược chốt được gọi).

Tôi thường giới thiệu hai cuốn sách về lĩnh vực này: Lập trình đồng thời trong JavaĐồng thời trong Java .


5

Tôi đã tạo một lớp trợ giúp chỉ cho điều này một thời gian trước. Hoạt động tuyệt vời:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
 * TimeOut class - used for stopping a thread that is taking too long
 * @author Peter Goransson
 *
 */
public class TimeOut {

    Thread interrupter;
    Thread target;
    long timeout;
    boolean success;
    boolean forceStop;

    CyclicBarrier barrier;

    /**
     * 
     * @param target The Runnable target to be executed
     * @param timeout The time in milliseconds before target will be interrupted or stopped
     * @param forceStop If true, will Thread.stop() this target instead of just interrupt() 
     */
    public TimeOut(Runnable target, long timeout, boolean forceStop) {      
        this.timeout = timeout;
        this.forceStop = forceStop;

        this.target = new Thread(target);       
        this.interrupter = new Thread(new Interrupter());

        barrier = new CyclicBarrier(2); // There will always be just 2 threads waiting on this barrier
    }

    public boolean execute() throws InterruptedException {  

        // Start target and interrupter
        target.start();
        interrupter.start();

        // Wait for target to finish or be interrupted by interrupter
        target.join();  

        interrupter.interrupt(); // stop the interrupter    
        try {
            barrier.await(); // Need to wait on this barrier to make sure status is set
        } catch (BrokenBarrierException e) {
            // Something horrible happened, assume we failed
            success = false;
        } 

        return success; // status is set in the Interrupter inner class
    }

    private class Interrupter implements Runnable {

        Interrupter() {}

        public void run() {
            try {
                Thread.sleep(timeout); // Wait for timeout period and then kill this target
                if (forceStop) {
                  target.stop(); // Need to use stop instead of interrupt since we're trying to kill this thread
                }
                else {
                    target.interrupt(); // Gracefully interrupt the waiting thread
                }
                System.out.println("done");             
                success = false;
            } catch (InterruptedException e) {
                success = true;
            }


            try {
                barrier.await(); // Need to wait on this barrier
            } catch (InterruptedException e) {
                // If the Child and Interrupter finish at the exact same millisecond we'll get here
                // In this weird case assume it failed
                success = false;                
            } 
            catch (BrokenBarrierException e) {
                // Something horrible happened, assume we failed
                success = false;
            }

        }

    }
}

Nó được gọi như thế này:

long timeout = 10000; // number of milliseconds before timeout
TimeOut t = new TimeOut(new PhotoProcessor(filePath, params), timeout, true);
try {                       
  boolean sucess = t.execute(); // Will return false if this times out
  if (!sucess) {
    // This thread timed out
  }
  else {
    // This thread ran completely and did not timeout
  }
} catch (InterruptedException e) {}  

3

Tôi gửi cho bạn một đoạn mã chỉ ra cách giải quyết vấn đề. Như ví dụ, tôi đang đọc một tệp. Bạn có thể sử dụng phương thức này cho một hoạt động khác, nhưng bạn cần triển khai phương thức kill () để hoạt động chính sẽ bị gián đoạn.

hy vọng nó giúp


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Main class
 * 
 * @author el
 * 
 */
public class Main {
    /**
     * Thread which perform the task which should be timed out.
     * 
     * @author el
     * 
     */
    public static class MainThread extends Thread {
        /**
         * For example reading a file. File to read.
         */
        final private File fileToRead;
        /**
         * InputStream from the file.
         */
        final private InputStream myInputStream;
        /**
         * Thread for timeout.
         */
        final private TimeOutThread timeOutThread;

        /**
         * true if the thread has not ended.
         */
        boolean isRunning = true;

        /**
         * true if all tasks where done.
         */
        boolean everythingDone = false;

        /**
         * if every thing could not be done, an {@link Exception} may have
         * Happens.
         */
        Throwable endedWithException = null;

        /**
         * Constructor.
         * 
         * @param file
         * @throws FileNotFoundException
         */
        MainThread(File file) throws FileNotFoundException {
            setDaemon(false);
            fileToRead = file;
            // open the file stream.
            myInputStream = new FileInputStream(fileToRead);
            // Instantiate the timeout thread.
            timeOutThread = new TimeOutThread(10000, this);
        }

        /**
         * Used by the {@link TimeOutThread}.
         */
        public void kill() {
            if (isRunning) {
                isRunning = false;
                if (myInputStream != null) {
                    try {
                        // close the stream, it may be the problem.
                        myInputStream.close();
                    } catch (IOException e) {
                        // Not interesting
                        System.out.println(e.toString());
                    }
                }
                synchronized (this) {
                    notify();
                }
            }
        }

        /**
         * The task which should be timed out.
         */
        @Override
        public void run() {
            timeOutThread.start();
            int bytes = 0;
            try {
                // do something
                while (myInputStream.read() >= 0) {
                    // may block the thread.
                    myInputStream.read();
                    bytes++;
                    // simulate a slow stream.
                    synchronized (this) {
                        wait(10);
                    }
                }
                everythingDone = true;
            } catch (IOException e) {
                endedWithException = e;
            } catch (InterruptedException e) {
                endedWithException = e;
            } finally {
                timeOutThread.kill();
                System.out.println("-->read " + bytes + " bytes.");
                isRunning = false;
                synchronized (this) {
                    notifyAll();
                }
            }
        }
    }

    /**
     * Timeout Thread. Kill the main task if necessary.
     * 
     * @author el
     * 
     */
    public static class TimeOutThread extends Thread {
        final long timeout;
        final MainThread controlledObj;

        TimeOutThread(long timeout, MainThread controlledObj) {
            setDaemon(true);
            this.timeout = timeout;
            this.controlledObj = controlledObj;
        }

        boolean isRunning = true;

        /**
         * If we done need the {@link TimeOutThread} thread, we may kill it.
         */
        public void kill() {
            isRunning = false;
            synchronized (this) {
                notify();
            }
        }

        /**
         * 
         */
        @Override
        public void run() {
            long deltaT = 0l;
            try {
                long start = System.currentTimeMillis();
                while (isRunning && deltaT < timeout) {
                    synchronized (this) {
                        wait(Math.max(100, timeout - deltaT));
                    }
                    deltaT = System.currentTimeMillis() - start;
                }
            } catch (InterruptedException e) {
                // If the thread is interrupted,
                // you may not want to kill the main thread,
                // but probably yes.
            } finally {
                isRunning = false;
            }
            controlledObj.kill();
        }
    }

    /**
     * Start the main task and wait for the end.
     * 
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        MainThread main = new MainThread(new File(args[0]));
        main.start();
        try {
            while (main.isRunning) {
                synchronized (main) {
                    main.wait(1000);
                }
            }
            long stop = System.currentTimeMillis();

            if (main.everythingDone)
                System.out.println("all done in " + (stop - start) + " ms.");
            else {
                System.out.println("could not do everything in "
                        + (stop - start) + " ms.");
                if (main.endedWithException != null)
                    main.endedWithException.printStackTrace();
            }
        } catch (InterruptedException e) {
            System.out.println("You've killed me!");
        }
    }
}

Trân trọng


3

Đây là lớp trợ giúp thực sự đơn giản của tôi để chạy hoặc gọi đoạn mã Java :-)

Điều này dựa trên câu trả lời tuyệt vời từ BalusC

package com.mycompany.util.concurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Calling {@link Callable#call()} or Running {@link Runnable#run()} code
 * with a timeout based on {@link Future#get(long, TimeUnit))}
 * @author pascaldalfarra
 *
 */
public class CallableHelper
{

    private CallableHelper()
    {
    }

    public static final void run(final Runnable runnable, int timeoutInSeconds)
    {
        run(runnable, null, timeoutInSeconds);
    }

    public static final void run(final Runnable runnable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        call(new Callable<Void>()
        {
            @Override
            public Void call() throws Exception
            {
                runnable.run();
                return null;
            }
        }, timeoutCallback, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, int timeoutInSeconds)
    {
        return call(callable, null, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        try
        {
            Future<T> future = executor.submit(callable);
            T result = future.get(timeoutInSeconds, TimeUnit.SECONDS);
            System.out.println("CallableHelper - Finished!");
            return result;
        }
        catch (TimeoutException e)
        {
            System.out.println("CallableHelper - TimeoutException!");
            if(timeoutCallback != null)
            {
                timeoutCallback.run();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        catch (ExecutionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            executor.shutdownNow();
            executor = null;
        }

        return null;
    }

}

2

Đoạn mã sau sẽ bắt đầu một thao tác trong một chuỗi riêng biệt, sau đó đợi tối đa 10 giây để thao tác hoàn tất. Nếu thao tác không hoàn thành kịp thời, mã sẽ cố gắng hủy thao tác, sau đó tiếp tục hoạt động vui vẻ. Ngay cả khi thao tác không thể bị hủy bỏ một cách dễ dàng, luồng cha sẽ không đợi luồng con kết thúc.

ExecutorService executorService = getExecutorService();
Future<SomeClass> future = executorService.submit(new Callable<SomeClass>() {
    public SomeClass call() {
        // Perform long-running task, return result. The code should check
        // interrupt status regularly, to facilitate cancellation.
    }
});
try {
    // Real life code should define the timeout as a constant or
    // retrieve it from configuration
    SomeClass result = future.get(10, TimeUnit.SECONDS);
    // Do something with the result
} catch (TimeoutException e) {
    future.cancel(true);
    // Perform other error handling, e.g. logging, throwing an exception
}

Các getExecutorService()phương pháp có thể được thực hiện trong một số cách khác nhau. Nếu bạn không có bất kỳ yêu cầu cụ thể nào, bạn có thể chỉ cần yêu cầu Executors.newCachedThreadPool()gộp luồng mà không có giới hạn trên về số lượng luồng.


Nhập khẩu cần những gì? Là gì SomeClassvà là Futuregì?
ADTC

2

Một điều mà tôi chưa thấy đề cập là việc giết các chủ đề nói chung là một Ý tưởng Xấu. Có những kỹ thuật để làm cho các phương thức luồng có thể hủy bỏ hoàn toàn , nhưng điều đó khác với việc chỉ giết một luồng sau một thời gian chờ.

Rủi ro với những gì bạn đang đề xuất là bạn có thể không biết luồng sẽ ở trạng thái nào khi bạn giết nó - vì vậy bạn có nguy cơ gây ra sự không ổn định. Một giải pháp tốt hơn là đảm bảo mã luồng của bạn không bị treo hoặc sẽ phản hồi tốt với yêu cầu hủy bỏ.


Nếu không có ngữ cảnh, một tuyên bố như của bạn nghe có vẻ quá hạn chế. Trong môi trường học tập, tôi thường xuyên phải kiểm tra thứ gì đó đến hết thời gian chờ, và khi nó xảy ra, tôi chỉ cần bỏ tất cả tính toán và ghi lại rằng thời gian chờ đã xảy ra. Có lẽ nó là hiếm trong ngành công nghiệp, nhưng vẫn ...
Alessandro S.

@AlessandroS: đó là một điểm hợp lý, mặc dù OP đã yêu cầu "các giải pháp tốt hơn", theo đó tôi cho rằng sự mạnh mẽ và độ tin cậy được ưu tiên hơn là bạo lực.
Dan Puzey,

2

Câu trả lời tuyệt vời của BalusC's:

nhưng Chỉ cần thêm rằng bản thân thời gian chờ không làm gián đoạn chính chuỗi. ngay cả khi bạn đang kiểm tra với while (! Thread.interrupt ()) trong tác vụ của mình. nếu bạn muốn đảm bảo rằng chuỗi đã dừng, bạn cũng nên đảm bảo rằng future.cancel () được gọi khi bắt được ngoại lệ thời gian chờ.

package com.stackoverflow.q2275443; 

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


public class Test { 
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try { 
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            //Without the below cancel the thread will continue to live 
            // even though the timeout exception thrown.
            future.cancel();
            System.out.println("Terminated!");
        } 

        executor.shutdownNow();
    } 
} 

class Task implements Callable<String> {
    @Override 
    public String call() throws Exception {
      while(!Thread.currentThread.isInterrupted()){
          System.out.println("Im still running baby!!");
      }          
    } 
} 

0

Tôi nghĩ câu trả lời chủ yếu phụ thuộc vào bản thân nhiệm vụ.

  • Nó có làm đi làm lại một nhiệm vụ không?
  • Có cần thiết thời gian chờ làm gián đoạn tác vụ hiện đang chạy ngay sau khi nó hết hạn không?

Nếu câu trả lời đầu tiên là có và câu trả lời thứ hai là không, bạn có thể giữ nó đơn giản như sau:

public class Main {

    private static final class TimeoutTask extends Thread {
        private final long _timeoutMs;
        private Runnable _runnable;

        private TimeoutTask(long timeoutMs, Runnable runnable) {
            _timeoutMs = timeoutMs;
            _runnable = runnable;
        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            while (System.currentTimeMillis() < (start + _timeoutMs)) {
                _runnable.run();
            }
            System.out.println("execution took " + (System.currentTimeMillis() - start) +" ms");
        }

    }

    public static void main(String[] args) throws Exception {
        new TimeoutTask(2000L, new Runnable() {

            @Override
            public void run() {
                System.out.println("doing something ...");
                try {
                    // pretend it's taking somewhat longer than it really does
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}

Nếu đây không phải là một tùy chọn, vui lòng thu hẹp yêu cầu của bạn - hoặc hiển thị một số mã.


0

Tôi đang tìm kiếm một ExecutorService có thể ngắt tất cả các Runnables đã hết thời gian thực thi bởi nó, nhưng không tìm thấy. Sau một vài giờ, tôi đã tạo một cái như bên dưới. Lớp này có thể được sửa đổi để tăng cường độ mạnh mẽ.

public class TimedExecutorService extends ThreadPoolExecutor {
    long timeout;
    public TimedExecutorService(int numThreads, long timeout, TimeUnit unit) {
        super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(numThreads + 1));
        this.timeout = unit.toMillis(timeout);
    }

    @Override
    protected void beforeExecute(Thread thread, Runnable runnable) {
        Thread interruptionThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Wait until timeout and interrupt this thread
                    Thread.sleep(timeout);
                    System.out.println("The runnable times out.");
                    thread.interrupt();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        interruptionThread.start();
    }
}

Sử dụng:

public static void main(String[] args) {

    Runnable abcdRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("abcdRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("abcdRunnable ended");
        }
    };

    Runnable xyzwRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("xyzwRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("xyzwRunnable ended");
        }
    };

    int numThreads = 2, timeout = 5;
    ExecutorService timedExecutor = new TimedExecutorService(numThreads, timeout, TimeUnit.SECONDS);
    timedExecutor.execute(abcdRunnable);
    timedExecutor.execute(xyzwRunnable);
    timedExecutor.shutdown();
}

0

Bây giờ, tôi gặp một vấn đề như thế này. Nó xảy ra để giải mã hình ảnh. Quá trình giải mã mất quá nhiều thời gian khiến màn hình bị đen. l thêm một bộ điều khiển thời gian: khi thời gian quá dài, sau đó bật lên từ Chủ đề hiện tại. Sau đây là sự khác biệt:

   ExecutorService executor = Executors.newSingleThreadExecutor();
   Future<Bitmap> future = executor.submit(new Callable<Bitmap>() {
       @Override
       public Bitmap call() throws Exception {
       Bitmap bitmap = decodeAndScaleBitmapFromStream(context, inputUri);// do some time consuming operation
       return null;
            }
       });
       try {
           Bitmap result = future.get(1, TimeUnit.SECONDS);
       } catch (TimeoutException e){
           future.cancel(true);
       }
       executor.shutdown();
       return (bitmap!= null);

0

Tôi đã từng gặp vấn đề tương tự. Vì vậy, tôi đã đưa ra một giải pháp đơn giản như thế này.

public class TimeoutBlock {

 private final long timeoutMilliSeconds;
    private long timeoutInteval=100;

    public TimeoutBlock(long timeoutMilliSeconds){
        this.timeoutMilliSeconds=timeoutMilliSeconds;
    }

    public void addBlock(Runnable runnable) throws Throwable{
        long collectIntervals=0;
        Thread timeoutWorker=new Thread(runnable);
        timeoutWorker.start();
        do{ 
            if(collectIntervals>=this.timeoutMilliSeconds){
                timeoutWorker.stop();
                throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
            }
            collectIntervals+=timeoutInteval;           
            Thread.sleep(timeoutInteval);

        }while(timeoutWorker.isAlive());
        System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
    }

    /**
     * @return the timeoutInteval
     */
    public long getTimeoutInteval() {
        return timeoutInteval;
    }

    /**
     * @param timeoutInteval the timeoutInteval to set
     */
    public void setTimeoutInteval(long timeoutInteval) {
        this.timeoutInteval = timeoutInteval;
    }
}

Đảm bảo rằng nếu khối không thực thi trong thời hạn. quá trình sẽ kết thúc và ném một ngoại lệ.

thí dụ :

try {
        TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
        Runnable block=new Runnable() {

            @Override
            public void run() {
                //TO DO write block of code 
            }
        };

        timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) {
        //catch the exception here . Which is block didn't execute within the time limit
    }

0

Trong giải pháp do BalusC đưa ra , chuỗi chính sẽ bị chặn trong khoảng thời gian chờ. Nếu bạn có một nhóm luồng với nhiều hơn một luồng, bạn sẽ cần cùng một số luồng bổ sung sẽ sử dụng Future.get (thời gian chờ dài, đơn vị TimeUnit) để chờ và đóng luồng nếu nó vượt quá khoảng thời gian chờ.

Một giải pháp chung cho vấn đề này là tạo một Trình trang trí ThreadPoolExecutor có thể thêm chức năng thời gian chờ. Lớp Decorator này nên tạo nhiều luồng như ThreadPoolExecutor có, và tất cả các luồng này chỉ nên được sử dụng để đợi và đóng ThreadPoolExecutor.

Lớp chung sẽ được triển khai như sau:

import java.util.List;
import java.util.concurrent.*;

public class TimeoutThreadPoolDecorator extends ThreadPoolExecutor {


    private final ThreadPoolExecutor commandThreadpool;
    private final long timeout;
    private final TimeUnit unit;

    public TimeoutThreadPoolDecorator(ThreadPoolExecutor threadpool,
                                      long timeout,
                                      TimeUnit unit ){
        super(  threadpool.getCorePoolSize(),
                threadpool.getMaximumPoolSize(),
                threadpool.getKeepAliveTime(TimeUnit.MILLISECONDS),
                TimeUnit.MILLISECONDS,
                threadpool.getQueue());

        this.commandThreadpool = threadpool;
        this.timeout=timeout;
        this.unit=unit;
    }

    @Override
    public void execute(Runnable command) {
        super.execute(() -> {
            Future<?> future = commandThreadpool.submit(command);
            try {
                future.get(timeout, unit);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (ExecutionException | TimeoutException e) {
                throw new RejectedExecutionException(e);
            } finally {
                future.cancel(true);
            }
        });
    }

    @Override
    public void setCorePoolSize(int corePoolSize) {
        super.setCorePoolSize(corePoolSize);
        commandThreadpool.setCorePoolSize(corePoolSize);
    }

    @Override
    public void setThreadFactory(ThreadFactory threadFactory) {
        super.setThreadFactory(threadFactory);
        commandThreadpool.setThreadFactory(threadFactory);
    }

    @Override
    public void setMaximumPoolSize(int maximumPoolSize) {
        super.setMaximumPoolSize(maximumPoolSize);
        commandThreadpool.setMaximumPoolSize(maximumPoolSize);
    }

    @Override
    public void setKeepAliveTime(long time, TimeUnit unit) {
        super.setKeepAliveTime(time, unit);
        commandThreadpool.setKeepAliveTime(time, unit);
    }

    @Override
    public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
        super.setRejectedExecutionHandler(handler);
        commandThreadpool.setRejectedExecutionHandler(handler);
    }

    @Override
    public List<Runnable> shutdownNow() {
        List<Runnable> taskList = super.shutdownNow();
        taskList.addAll(commandThreadpool.shutdownNow());
        return taskList;
    }

    @Override
    public void shutdown() {
        super.shutdown();
        commandThreadpool.shutdown();
    }
}

Trình trang trí trên có thể được sử dụng như sau:

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args){

        long timeout = 2000;

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(true));

        threadPool = new TimeoutThreadPoolDecorator( threadPool ,
                timeout,
                TimeUnit.MILLISECONDS);


        threadPool.execute(command(1000));
        threadPool.execute(command(1500));
        threadPool.execute(command(2100));
        threadPool.execute(command(2001));

        while(threadPool.getActiveCount()>0);
        threadPool.shutdown();


    }

    private static Runnable command(int i) {

        return () -> {
            System.out.println("Running Thread:"+Thread.currentThread().getName());
            System.out.println("Starting command with sleep:"+i);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                System.out.println("Thread "+Thread.currentThread().getName()+" with sleep of "+i+" is Interrupted!!!");
                return;
            }
            System.out.println("Completing Thread "+Thread.currentThread().getName()+" after sleep of "+i);
        };

    }
}
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.