วิธีการหมดเวลาของเธรด


255

ฉันต้องการเรียกใช้เธรดในระยะเวลาที่แน่นอน หากยังไม่แล้วเสร็จภายในเวลานั้นฉันต้องการฆ่าทิ้งข้อยกเว้นหรือจัดการบางอย่าง จะทำอย่างไร?

วิธีหนึ่งในการทำตามที่ฉันคิดออกจากหัวข้อนี้ คือการใช้ TimerTask ภายในเมธอด run () ของเธรด

มีวิธีแก้ปัญหาที่ดีกว่านี้หรือไม่?

 
แก้ไข: เพิ่มรางวัลตามที่ฉันต้องการคำตอบที่ชัดเจน รหัส ExecutorService ที่ระบุด้านล่างไม่สามารถแก้ไขปัญหาของฉันได้ ทำไมฉันถึงควรหลับ () หลังจากทำการประมวลผล (บางรหัส - ฉันไม่มีส่วนจัดการกับรหัสนี้) หากรหัสเสร็จสมบูรณ์และ sleep () ถูกขัดจังหวะจะเป็น timeOut ได้อย่างไร

งานที่ต้องดำเนินการไม่ได้อยู่ในการควบคุมของฉัน มันอาจเป็นรหัสใด ๆ ปัญหาคือรหัสชิ้นส่วนนี้อาจทำงานเป็นวงวนไม่สิ้นสุด ฉันไม่ต้องการที่จะเกิดขึ้น ดังนั้นฉันแค่ต้องการที่จะทำงานในหัวข้อแยก เธรดหลักต้องรอจนกว่าเธรดนั้นจะเสร็จสิ้นและจำเป็นต้องทราบสถานะของงาน (เช่นไม่ว่าจะหมดเวลาหรือมีข้อยกเว้นเกิดขึ้นหรือประสบความสำเร็จ) หากงานเข้าสู่วงวนไม่สิ้นสุดเธรดหลักของฉันจะคอยเรื่อย ๆ ซึ่งไม่ใช่สถานการณ์ในอุดมคติ


แก้ไข: การเพิ่มรางวัลตามที่ฉันต้องการคำตอบที่ชัดเจนยิ่งขึ้น รหัส ExecutorService ที่ระบุด้านล่างไม่สามารถแก้ไขปัญหาของฉันได้ ทำไมฉันถึงควรหลับ () หลังจากรันโค้ดของฉัน? หากรหัสเสร็จสมบูรณ์และ sleep () ถูกขัดจังหวะจะเป็น timeOut ได้อย่างไร
java_geek

7
นั่นsleep()เป็นเพียงต้นขั้วเพื่อแสดง "งานที่ต้องใช้เวลานาน" เพียงแค่เปลี่ยนมันได้กับงานที่แท้จริงของคุณ;)
BalusC

1
... งาน "ใช้เวลานาน" ที่เกิดขึ้นในการตอบสนองต่อการinterrupt()โทรบนเธรด ... การโทรแบบ "ปิดกั้น" ไม่ใช่ทั้งหมดเนื่องจากฉันพยายามชี้ให้เห็นในคำตอบของฉัน ข้อมูลเฉพาะของงานที่คุณพยายามยกเลิกนั้นสร้างความแตกต่างอย่างมากในแนวทางที่ควรนำมาใช้ ข้อมูลเพิ่มเติมเกี่ยวกับงานจะเป็นประโยชน์
erickson

หากคำตอบเหล่านี้ไม่สามารถแก้ปัญหาได้ฉันเดาว่ารายละเอียดเพิ่มเติม / รหัสควรช่วยตอบ
Elister

เธรดเหล่านี้ที่คุณต้องการ จำกัด เวลา พวกเขากำลังปิดกั้นการโทรหรือพวกเขาอยู่ในวงที่คุณสามารถตรวจสอบตัวแปรบางอย่างเพื่อดูว่าถึงเวลาที่จะเลิก?
Scott Smith

คำตอบ:


376

แทนที่จะใช้ExecutorServiceแทนTimerนี่คือ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!";
    }
}

เล่นโดยใช้วิธีtimeoutโต้แย้งFuture#get()เช่นเพิ่มเป็น 5 และคุณจะเห็นว่าเธรดเสร็จสิ้น คุณสามารถสกัดกั้นการหมดเวลาในcatch (TimeoutException e)บล็อก

UPDATE:เพื่อชี้แจงความเข้าใจผิดแนวความคิดที่sleep()จะไม่จำเป็นต้องใช้ มันถูกใช้เพื่อวัตถุประสงค์ SSCCE / การสาธิตเท่านั้น เพียงแค่ทำของคุณsleep()งานระยะยาวมีสิทธิในสถานที่ของ ภายในภารกิจที่รันนานคุณควรตรวจสอบว่าเธรดไม่ถูกขัดจังหวะดังต่อไปนี้:

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

24
แทนที่Thread.sleep(4000)ด้วยคำสั่งที่ใช้เวลานาน ๆ และตัวอย่างจะไม่ทำงาน ในคำอื่น ๆ ตัวอย่างนี้จะทำงานเฉพาะในกรณีที่Taskถูกออกแบบมาเพื่อทำความเข้าใจThread.isInterrupted()การเปลี่ยนแปลงสถานะ
yegor256

@BalusC ฉันลองวิธีการนี้พยายามที่จะยุติเธรดของฉัน แต่ไม่สามารถใช้งานได้ คุณสามารถตรวจสอบได้ที่นี่: stackoverflow.com/questions/35553420/…
syfantid

InterruptedException ถูกจัดลำดับอย่างไรโดย future.cancel (จริง) ได้รับการจัดการอย่างไร
bolei

1
มีคนจำนวนหนึ่งแสดงความคิดเห็นเกี่ยวกับชื่อแพ็คเกจและนี่คืออีก +1 นั่นเป็นทักษะที่ดีที่ถูกฝัง ขอบคุณ!
Ashwin Tumma

@BalusC ฉันมีข้อสงสัยว่าอนาคตจะดำเนินการพร้อมกันหรือไม่และหากใช้เวลาเกินกว่าที่กำหนดไว้ล่วงหน้าก็จะถูกยกเลิก อีกอย่างมันจะดำเนินการในอนาคตในขณะที่เรากำลังนับเวลา ... ขอขอบคุณ
Adeel Ahmad

49

ไม่มีวิธีที่เชื่อถือได้ 100% สำหรับงานเก่า ๆ งานต้องเขียนด้วยความสามารถนี้ในใจ

ไลบรารี Core Java เช่นExecutorServiceยกเลิกภารกิจแบบอะซิงโครนัสด้วยการinterrupt()เรียกใช้เธรดผู้ปฏิบัติงาน ตัวอย่างเช่นหากงานมีลูปบางประเภทคุณควรตรวจสอบสถานะอินเตอร์รัปต์ในแต่ละการวนซ้ำ หากงานกำลังดำเนินการ I / O ก็ควรขัดจังหวะด้วยเช่นกันและการตั้งค่านั้นอาจยุ่งยาก ไม่ว่าในกรณีใดโปรดจำไว้ว่ารหัสต้องตรวจสอบการขัดจังหวะ การขัดจังหวะไม่จำเป็นต้องทำอะไร

แน่นอนถ้างานของคุณคือการวนรอบแบบง่ายคุณสามารถตรวจสอบเวลาปัจจุบันในแต่ละการวนซ้ำและยกเลิกเมื่อการหมดเวลาที่ระบุได้ผ่านไปแล้ว ไม่จำเป็นต้องมีเธรดผู้ปฏิบัติงานในกรณีนี้


ในประสบการณ์ของฉันรหัสเดียวที่ไม่ตอบสนองต่อการเริ่มต้นถูกขัดจังหวะคือการบล็อกในรหัสพื้นเมือง (กำลังรอระบบปฏิบัติการ)
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersenฉันเห็นด้วย แต่นั่นเป็นรหัสจำนวนมาก ประเด็นของฉันคือไม่มีกลไกวัตถุประสงค์ทั่วไปสำหรับสิ่งนี้ คุณต้องเข้าใจนโยบายการหยุดชะงักของงาน
erickson

@erickson ฉันเห็นด้วยกับคุณ สำหรับคำตอบนั้นจะต้องมีนโยบายการยกเลิกที่กำหนดไว้สำหรับแต่ละงานหากคุณสนใจที่จะหยุดมัน หรือเธรดควรระวังสิ่งที่ควรทำเมื่อถูกขัดจังหวะ ท้ายที่สุดการขัดจังหวะและการหยุดเธรดใด ๆ เป็นเพียงคำขอที่เธรดเป้าหมายอาจยอมรับหรือปฏิเสธดังนั้นจึงควรเขียนภารกิจที่คำนึงถึงเรื่องนี้
AKS

ไม่สามารถ executorservice เลือกที่จะเรียกใช้งานบนเธรดการโทรได้หรือไม่ ผู้ให้บริการยังอาจเลือกที่จะดำเนินงานในอนาคต?
filthy_wizard

@ user1232726 execute()วิธีการของส่วนต่อประสานผู้ปกครองExecutorอาจเรียกใช้งานในหัวข้อการโทร ไม่มีคำสั่งที่คล้ายกันสำหรับsubmit()วิธีการของอินสแตนซ์ที่ExecutorServiceกลับมา Futureความหมายของบริการคือมีเธรดผู้ปฏิบัติงานที่ต้องล้างข้อมูลผ่านการปิดระบบและงานที่ดำเนินการแบบอะซิงโครนัส ที่กล่าวว่าไม่มีอะไรในสัญญาที่ระบุว่าExecutorServiceเป็นสิ่งต้องห้ามในการดำเนินงานในหัวข้อการส่ง; การค้ำประกันเหล่านั้นมาจากการติดตั้ง API เช่นExecutorsโรงงาน
erickson

13

พิจารณาใช้ตัวอย่างของExecutorService ทั้งสองinvokeAll()และinvokeAny()วิธีการมีให้กับtimeoutพารามิเตอร์

เธรดปัจจุบันจะบล็อกจนกว่าเมธอดจะเสร็จสมบูรณ์ (ไม่แน่ใจว่าเป็นสิ่งที่ต้องการหรือไม่) เนื่องจากงานเสร็จตามปกติหรือหมดเวลา คุณสามารถตรวจสอบรายการที่ส่งคืนFutureเพื่อพิจารณาว่าเกิดอะไรขึ้น


9

สมมติว่ารหัสเธรดอยู่นอกการควบคุมของคุณ:

จากเอกสาร Java ที่กล่าวถึงข้างต้น:

เกิดอะไรขึ้นถ้าเธรดไม่ตอบสนองต่อเธรดขัดจังหวะ?

ในบางกรณีคุณสามารถใช้เทคนิคเฉพาะแอปพลิเคชัน ตัวอย่างเช่นหากเธรดกำลังรอซ็อกเก็ตที่รู้จักคุณสามารถปิดซ็อกเก็ตเพื่อทำให้เธรดกลับมาทันที น่าเสียดายที่ไม่มีเทคนิคใด ๆ ที่ใช้งานได้ทั่วไป ควรสังเกตว่าในทุกสถานการณ์ที่เธรดที่รออยู่ไม่ตอบสนองต่อ Thread.interrupt มันจะไม่ตอบสนองต่อ Thread.stop เช่นกัน กรณีดังกล่าวรวมถึงการโจมตีแบบปฏิเสธการให้บริการโดยเจตนาและการดำเนินการ I / O ซึ่ง thread.stop และเธรดหยุดทำงานไม่ถูกต้อง

บรรทัดล่างสุด:

ตรวจสอบให้แน่ใจว่าเธรดทั้งหมดสามารถถูกขัดจังหวะหรืออื่น ๆ ที่คุณต้องการความรู้เฉพาะของเธรดเช่นมีการตั้งค่าสถานะ บางทีคุณอาจต้องการให้งานมอบให้คุณพร้อมรหัสที่จำเป็นในการหยุด - กำหนดอินเทอร์เฟซด้วยstop()วิธีการ คุณสามารถเตือนเมื่อคุณไม่สามารถหยุดงานได้


8

BalusC กล่าวว่า:

อัปเดต: เพื่อชี้แจงความเข้าใจผิดเกี่ยวกับแนวคิดไม่จำเป็นต้องใช้โหมดสลีป () มันถูกใช้เพื่อวัตถุประสงค์ SSCCE / การสาธิตเท่านั้น เพียงแค่ทำงานที่ยาวนานของคุณที่นั่นแทนการนอนหลับ ()

แต่ถ้าคุณแทนที่Thread.sleep(4000);ด้วยfor (int i = 0; i < 5E8; i++) {}มันก็ไม่ได้คอมไพล์เพราะวงว่างเปล่าไม่ได้โยนInterruptedExceptionแล้วมันไม่ได้รวบรวมเพราะห่วงที่ว่างเปล่าไม่ได้โยน

และเพื่อให้ด้ายขัดจังหวะมันจะต้องโยน InterruptedExceptionก็ต้องโยน

ดูเหมือนว่าจะเป็นปัญหาร้ายแรงสำหรับฉัน ฉันไม่เห็นวิธีการปรับคำตอบนี้ให้ทำงานกับงานที่ต้องใช้เวลานานโดยทั่วไป

แก้ไขเพื่อเพิ่ม: ฉันได้ถามคำถามนี้ใหม่เป็นคำถามใหม่: [ ขัดจังหวะเธรดหลังจากเวลาที่กำหนดคงต้องทิ้ง InterruptedException หรือไม่ ]


วิธีที่ฉันทำก็คือการเพิ่ม rowthrows Exception public ใน public class <T> call {} method
Roberto Linares

5

ฉันคิดว่าคุณควรดูกลไกการจัดการภาวะพร้อมกันที่เหมาะสม (กระทู้ที่วิ่งเข้าไปในลูปที่ไม่มีที่สิ้นสุดไม่ได้เสียงดีต่อ se, btw) ตรวจสอบให้แน่ใจว่าคุณอ่านเล็กน้อยเกี่ยวกับหัวข้อ"ฆ่า" หรือ "หยุด"เล็กน้อย

สิ่งที่คุณกำลังอธิบายฟังดูคล้ายกับ "นัดพบ" ดังนั้นคุณอาจต้องการดูCyclicBarrier CyclicBarrier

อาจมีโครงสร้างอื่น ๆ (เช่นใช้CountDownLatchเป็นต้น) ที่สามารถแก้ไขปัญหาของคุณได้ (หนึ่งเธรดที่รอด้วยการหมดเวลาใช้งานของกลอน) อีกอันควรนับลงสลักถ้ามันทำงานเสร็จซึ่งจะปล่อยเธรดแรกของคุณหลังจากนั้น หมดเวลาหรือเมื่อมีการเรียกใช้การนับถอยหลัง)

ผมมักจะแนะนำหนังสือสองเล่มในพื้นที่นี้: การเขียนโปรแกรมพร้อมกันใน JavaและJava Concurrency ในการปฏิบัติ


5

ฉันสร้างคลาสผู้ช่วยเมื่อไม่นานมานี้ ใช้งานได้ดี:

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

        }

    }
}

มันถูกเรียกว่าสิ่งนี้:

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

ฉันโพสต์โค้ดให้คุณซึ่งจะแสดงวิธีการแก้ปัญหา ตัวอย่างที่ฉันอ่านไฟล์ คุณสามารถใช้วิธีนี้สำหรับการดำเนินการอื่น แต่คุณต้องใช้วิธีการ kill () เพื่อให้การดำเนินการหลักถูกขัดจังหวะ

หวังว่ามันจะช่วย


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!");
        }
    }
}

ความนับถือ


3

นี่เป็นเรื่องง่ายมากที่จะใช้คลาสผู้ช่วยในการเรียกใช้หรือเรียกส่วนของรหัส Java :-)

นี่คือคำตอบที่ยอดเยี่ยมจาก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

ตัวอย่างต่อไปนี้จะเริ่มการทำงานในเธรดแยกต่างหากจากนั้นรอนานถึง 10 วินาทีเพื่อให้การดำเนินการเสร็จสมบูรณ์ หากการดำเนินการไม่เสร็จตามกำหนดเวลารหัสจะพยายามยกเลิกการดำเนินการจากนั้นดำเนินการต่อด้วยวิธีที่สนุกสนาน แม้ว่าการดำเนินการจะไม่สามารถยกเลิกได้อย่างง่ายดายเธรดหลักจะไม่รอให้เธรดย่อยสิ้นสุด

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
}

getExecutorService()วิธีการสามารถดำเนินการในหลายวิธี หากคุณไม่มีข้อกำหนดเฉพาะคุณสามารถเรียกExecutors.newCachedThreadPool()ใช้การรวมเธรดโดยไม่มีการ จำกัด จำนวนเธรด


อะไรคือการนำเข้าที่จำเป็น? สิ่งที่เป็นSomeClassและFuture?
ADTC

2

สิ่งหนึ่งที่ฉันไม่เคยเห็นมาก่อนกล่าวคือการฆ่าเธรดมักเป็นแนวคิดที่ไม่ดี มีเทคนิคสำหรับการทำวิธีเธรดยกเลิกอย่างหมดจดแต่นั่นแตกต่างจากการฆ่าเธรดหลังจากหมดเวลา

ความเสี่ยงกับสิ่งที่คุณกำลังแนะนำคือคุณอาจไม่รู้ว่าสถานะของเธรดนั้นจะเป็นอย่างไรเมื่อคุณฆ่ามัน - ดังนั้นคุณจึงเสี่ยงต่อการเกิดความไม่แน่นอน ทางออกที่ดีกว่าคือการทำให้แน่ใจว่าโค้ดเธรดของคุณไม่ได้หยุดทำงานหรือจะตอบสนองต่อคำขอยกเลิกอย่างดี


หากไม่มีบริบทข้อความของคุณจะฟังดูเข้มงวดเกินไป ในสภาพแวดล้อมทางวิชาการฉันมีความจำเป็นต้องทดสอบบางอย่างจนหมดเวลาและเมื่อมันเกิดขึ้นฉันก็แค่ทำการคำนวณและบันทึกทั้งหมดที่เกิดขึ้นหมดเวลา อาจเป็นเรื่องยากในอุตสาหกรรม แต่ยัง ...
Alessandro S.

@ AlessandroS: นั่นเป็นประเด็นที่สมเหตุสมผลถึงแม้ว่า OP จะขอ "การแก้ปัญหาที่ดีกว่า" ซึ่งฉันได้หมายความว่าความแข็งแกร่งและความน่าเชื่อถือเป็นที่ต้องการมากกว่ากำลังดุร้าย
Dan Puzey

2

คำตอบที่ยอดเยี่ยมโดย BalusC's:

แต่เพียงเพื่อเพิ่มว่าการหมดเวลาของตัวเองไม่ได้ขัดขวางเธรดเอง แม้ว่าคุณจะตรวจสอบในขณะที่ (! Thread.interrupted ()) ในงานของคุณ ถ้าคุณต้องการที่จะทำให้แน่ใจว่าเธรดหยุดคุณควรตรวจสอบให้แน่ใจว่า future.cancel () ถูกเรียกใช้เมื่อมีการยกเว้นการหมดเวลา

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

ฉันคิดว่าคำตอบส่วนใหญ่ขึ้นอยู่กับงานของตัวเอง

  • มันกำลังทำภารกิจหนึ่งซ้ำแล้วซ้ำอีก?
  • จำเป็นหรือไม่ที่การหมดเวลาใช้งานจะขัดจังหวะงานที่กำลังทำงานอยู่ทันทีหลังจากหมดอายุหรือไม่

หากคำตอบแรกคือใช่และคำตอบที่สองคือไม่คุณสามารถพูดง่ายๆได้ดังนี้

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();
    }
}

หากนี่ไม่ใช่ตัวเลือกโปรดจำกัดความต้องการของคุณ - หรือแสดงรหัสบางส่วน


0

ฉันกำลังมองหา ExecutorService ที่สามารถขัดจังหวะหมดเวลาทั้งหมด Runnables ดำเนินการโดยมัน แต่ไม่พบ หลังจากนั้นไม่กี่ชั่วโมงฉันก็สร้างมันขึ้นมาด้านล่าง คลาสนี้สามารถแก้ไขได้เพื่อเพิ่มความแข็งแกร่ง

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();
    }
}

การใช้งาน:

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

ตอนนี้ฉันพบปัญหาเช่นนี้แล้ว มันเกิดขึ้นกับถอดรหัสภาพ กระบวนการถอดรหัสใช้เวลานานเกินไปที่หน้าจอจะเป็นสีดำ l เพิ่มตัวควบคุมเวลา: เมื่อเวลานานเกินไปจากนั้นจะปรากฏขึ้นจากเธรดปัจจุบัน ต่อไปนี้คือความแตกต่าง:

   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

ผมมีปัญหาเหมือนกัน. ดังนั้นฉันจึงคิดวิธีง่ายๆเช่นนี้

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

รับประกันได้ว่าหากบล็อกไม่ได้ดำเนินการภายในระยะเวลาที่กำหนด กระบวนการจะยุติและส่งข้อยกเว้น

ตัวอย่าง:

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

ในโซลูชันที่กำหนดโดยBalusCเธรดหลักจะยังคงถูกบล็อกในช่วงระยะหมดเวลา หากคุณมีกลุ่มเธรดที่มีมากกว่าหนึ่งเธรดคุณจะต้องมีจำนวนเธรดเพิ่มเติมเท่าเดิมที่จะใช้Future.get (การหมดเวลานานหน่วย TimeUnit)การบล็อกการโทรเพื่อรอและปิดเธรดหากเกินระยะเวลาหมดเวลา

วิธีแก้ปัญหาทั่วไปสำหรับปัญหานี้คือการสร้าง ThreadPoolExecutor Decorator ที่สามารถเพิ่มฟังก์ชันการหมดเวลา คลาส Decorator นี้ควรสร้างเธรดได้มากเท่าที่ ThreadPoolExecutor มีและเธรดเหล่านี้ทั้งหมดควรใช้เพื่อรอและปิด ThreadPoolExecutor เท่านั้น

คลาสทั่วไปควรดำเนินการดังนี้:

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();
    }
}

มัณฑนากรด้านบนสามารถใช้ดังนี้:

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);
        };

    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.