Cara mengatur waktu utas

255

Saya ingin menjalankan utas untuk jangka waktu tertentu. Jika tidak selesai dalam waktu itu, saya ingin membunuhnya, membuang beberapa pengecualian, atau menanganinya dengan cara tertentu. Bagaimana itu bisa dilakukan?

Salah satu cara melakukannya seperti yang saya ketahui dari utas ini adalah dengan menggunakan TimerTask di dalam metode run () dari Thread.

Apakah ada solusi yang lebih baik untuk ini?

 
EDIT: Menambahkan karunia karena saya membutuhkan jawaban yang lebih jelas. Kode ExecutorService yang diberikan di bawah ini tidak mengatasi masalah saya. Mengapa saya harus tidur () setelah mengeksekusi (beberapa kode - saya tidak memiliki pegangan atas potongan kode ini)? Jika kode selesai dan sleep () terputus, bagaimana bisa itu timeout?

Tugas yang perlu dijalankan tidak dalam kendali saya. Itu bisa berupa potongan kode. Masalahnya adalah bagian kode ini mungkin mengalami infinite loop. Saya tidak ingin itu terjadi. Jadi, saya hanya ingin menjalankan tugas itu di utas terpisah. Utas induk harus menunggu hingga utas itu selesai dan perlu mengetahui status tugas (yaitu apakah waktunya habis atau beberapa pengecualian terjadi atau jika berhasil). Jika tugas masuk ke loop tak terbatas, utas orang tua saya terus menunggu tanpa batas, yang bukan situasi ideal.

java_geek
sumber
EDIT: Menambahkan karunia karena saya membutuhkan jawaban yang lebih jelas. kode ExecutorService yang diberikan di bawah ini tidak mengatasi masalah saya. Mengapa saya harus tidur () setelah menjalankan kode saya? Jika kode selesai dan sleep () terputus, bagaimana bisa itu timeout?
java_geek
7
Itu sleep()hanya sebuah rintisan untuk mewakili "tugas berjalan lama". Ganti saja dengan tugas Anda yang sebenarnya;)
BalusC
1
... "tugas berjalan lama" yang terjadi untuk menanggapi interrupt()panggilan pada utasnya ... tidak semua "memblokir" panggilan, seperti yang saya coba tunjukkan dalam jawaban saya. Spesifik dari tugas yang Anda coba batalkan membuat perbedaan besar dalam pendekatan yang harus digunakan. Informasi lebih lanjut tentang tugas akan sangat membantu.
erickson
Jika jawaban ini tidak menyelesaikan masalah, maka saya kira lebih detail / kode akan membantu menjawab.
Elister
Utas ini yang ingin Anda batasi waktu; apakah mereka membuat panggilan pemblokiran, atau apakah mereka dalam satu lingkaran di mana Anda dapat dengan mudah memeriksa beberapa variabel untuk melihat apakah sudah waktunya untuk berhenti?
Scott Smith

Jawaban:

376

Memang lebih baik digunakan ExecutorServicedaripada Timer, inilah 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!";
    }
}

Mainkan sedikit dengan timeoutargumen dalam Future#get()metode, mis. Tingkatkan menjadi 5 dan Anda akan melihat bahwa utas selesai. Anda dapat mencegat batas waktu di catch (TimeoutException e)blok.

Memperbarui: untuk memperjelas kesalahpahaman konseptual, yang sleep()adalah tidak diperlukan. Itu hanya digunakan untuk tujuan SSCCE / demonstrasi. Lakukan saja tugas jangka panjang Anda di sana sleep(). Di dalam tugas jangka panjang Anda, Anda harus memeriksa apakah utas tidak terputus sebagai berikut:

while (!Thread.interrupted()) {
    // Do your long running task here.
}
BalusC
sumber
24
Ganti Thread.sleep(4000)dengan beberapa pernyataan jangka panjang lainnya dan contohnya tidak akan berfungsi. Dengan kata lain, contoh ini hanya akan berfungsi jika Taskdirancang untuk memahami Thread.isInterrupted()perubahan status.
yegor256
@ BalusC Saya mencoba pendekatan ini mencoba menghentikan utas saya, tetapi tidak berhasil. Anda dapat memeriksanya di sini: stackoverflow.com/questions/35553420/…
syfantid
Bagaimana InterruptedException ditangani oleh future.cancel (benar) ditangani?
bolei
1
sejumlah orang telah berkomentar tentang nama paket, dan inilah +1 lainnya untuk itu. Keterampilan yang bagus untuk diserap. Terima kasih!
Ashwin Tumma
@ BalusC Saya ragu, apakah Masa Depan akan berjalan secara serempak dan jika dibutuhkan lebih dari waktu yang telah ditentukan maka akan diakhiri. Jika tidak, maka akan dijalankan di masa mendatang sementara itu kami mengandalkan waktu ... Terima kasih
Adeel Ahmad
49

Tidak ada 100% cara andal untuk melakukan ini untuk tugas lama apa pun. Tugas harus ditulis dengan kemampuan ini dalam pikiran.

Pustaka Java Inti suka ExecutorServicemembatalkan tugas yang tidak sinkron dengan interrupt()panggilan pada utas pekerja. Jadi, misalnya, jika tugas tersebut berisi semacam loop, Anda harus memeriksa status interupsi pada setiap iterasi. Jika tugasnya adalah melakukan operasi I / O, mereka juga harus interruptible — dan pengaturannya bisa rumit. Bagaimanapun, ingatlah bahwa kode harus secara aktif memeriksa interupsi; mengatur interupsi tidak selalu melakukan apa pun.

Tentu saja, jika tugas Anda adalah loop sederhana, Anda bisa memeriksa waktu saat ini di setiap iterasi dan menyerah ketika batas waktu yang ditentukan telah berlalu. Dalam hal ini tidak diperlukan utas pekerja.

erickson
sumber
Dalam pengalaman saya, satu-satunya kode yang tidak bereaksi untuk mulai terganggu adalah pemblokiran dalam kode asli (menunggu sistem operasi).
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Saya setuju, tapi itu banyak kode. Maksud saya adalah bahwa tidak ada mekanisme tujuan umum untuk ini; Anda harus memahami kebijakan interupsi tugas.
erickson
@erickson, saya setuju dengan Anda. Untuk jawaban langsung, Harus ada kebijakan pembatalan yang ditentukan untuk setiap tugas, jika Anda tertarik untuk menghentikannya dengan cara itu. Atau utas harus menyadari apa yang seharusnya dilakukan ketika terputus. Lagi pula, menyela dan menghentikan utas apa pun hanyalah permintaan yang mungkin diterima atau ditolak utas target, jadi lebih baik menulis tugas dengan mengingat hal ini.
AKS
tidak dapat executorservice memilih untuk menjalankan tugas pada utas panggilan? juga executorservice dapat memilih untuk mengeksekusi tugas di masa depan?
filthy_wizard
@ user1232726 execute()Metode antarmuka induk, Executordapat menjalankan tugas di utas panggilan. Tidak ada pernyataan serupa untuk submit()metode ExecutorServiceyang mengembalikan Futurecontoh. Implikasi dari layanan ini adalah bahwa ada utas pekerja yang harus dibersihkan melalui shutdown, dan bahwa tugas dijalankan secara tidak sinkron. Yang mengatakan, tidak ada dalam kontrak yang mengatakan bahwa ExecutorServicedilarang melakukan tugas di utas pengajuan; jaminan tersebut berasal dari API implementasi, seperti Executorspabrik.
erickson
13

Pertimbangkan untuk menggunakan instance dari ExecutorService . Keduanya invokeAll()dan invokeAny()metode tersedia dengan timeoutparameter.

Utas saat ini akan memblokir hingga metode selesai (tidak yakin apakah ini diinginkan) baik karena tugas diselesaikan secara normal atau batas waktu tercapai. Anda dapat memeriksa yang dikembalikan Futureuntuk menentukan apa yang terjadi.

Drew Wills
sumber
9

Dengan asumsi kode utas di luar kendali Anda:

Dari dokumentasi Java yang disebutkan di atas:

Bagaimana jika sebuah utas tidak menanggapi Utas.interupsi?

Dalam beberapa kasus, Anda dapat menggunakan trik khusus aplikasi. Misalnya, jika utas menunggu pada soket yang dikenal, Anda dapat menutup soket untuk menyebabkan utas segera kembali. Sayangnya, sebenarnya tidak ada teknik yang bekerja secara umum. Perlu dicatat bahwa dalam semua situasi di mana utas tunggu tidak menanggapi Utas.interupsi, utas juga tidak akan merespons pada Thread.stop. Kasus-kasus tersebut termasuk serangan penolakan layanan yang disengaja, dan operasi I / O di mana thread.stop dan thread.interrupt tidak berfungsi dengan baik.

Intinya:

Pastikan semua utas dapat diinterupsi, atau Anda perlu pengetahuan khusus tentang utas - seperti memiliki bendera untuk ditetapkan. Mungkin Anda bisa meminta tugas diberikan kepada Anda bersama dengan kode yang diperlukan untuk menghentikannya - mendefinisikan antarmuka dengan stop()metode. Anda juga dapat memperingatkan ketika Anda gagal menghentikan tugas.

Peter Tseng
sumber
8

BalusC berkata:

Pembaruan: untuk memperjelas kesalahpahaman konseptual, sleep () tidak diperlukan. Itu hanya digunakan untuk tujuan SSCCE / demonstrasi. Lakukan saja tugas jangka panjang Anda di tempat tidur ().

Tetapi jika Anda mengganti Thread.sleep(4000);dengan for (int i = 0; i < 5E8; i++) {}maka itu tidak dikompilasi, karena loop kosong tidak melempar InterruptedException.

Dan agar utas itu bisa terputus, ia perlu melempar InterruptedException.

Ini sepertinya masalah serius bagi saya. Saya tidak bisa melihat bagaimana menyesuaikan jawaban ini untuk bekerja dengan tugas jangka panjang yang umum.

Diedit untuk menambahkan: Saya mengajukan kembali ini sebagai pertanyaan baru: [ memotong utas setelah waktu yang ditentukan, apakah harus membuang InterruptedException? ]

pengguna1310503
sumber
Cara saya melakukannya adalah dengan menambahkan 'Pengecualian' di kelas publik metode <T> panggilan {}
Roberto Linares
5

Saya pikir Anda harus melihat pada mekanisme penanganan konkurensi yang tepat (utas yang berjalan ke loop tak terbatas tidak terdengar bagus, btw). Pastikan Anda membaca sedikit tentang topik Utas "membunuh" atau "menghentikan" .

Apa yang Anda gambarkan, terdengar sangat mirip "pertemuan", jadi Anda mungkin ingin melihat CyclicBarrier .

Mungkin ada konstruksi lain (seperti menggunakan CountDownLatch misalnya) yang dapat menyelesaikan masalah Anda (satu utas menunggu dengan batas waktu untuk kait, yang lain harus menghitung mundur kait jika telah berhasil, yang akan melepaskan utas pertama Anda setelah batas waktu atau ketika hitung mundur dipanggil).

Saya biasanya merekomendasikan dua buku di bidang ini: Pemrograman Serentak di Jawa dan Java Concurrency dalam Praktek .

Diet
sumber
5

Saya membuat kelas pembantu hanya untuk ini beberapa waktu lalu. Bekerja sangat baik:

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

        }

    }
}

Disebut seperti ini:

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) {}  
Peter Goransson
sumber
3

Saya memposting Anda sepotong kode yang menunjukkan cara bagaimana menyelesaikan masalah. Sebagai contoh saya membaca file. Anda bisa menggunakan metode ini untuk operasi lain, tetapi Anda perlu mengimplementasikan metode kill () sehingga operasi utama akan terganggu.

semoga membantu


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

Salam

Elou
sumber
3

Inilah saya yang sangat sederhana untuk menggunakan kelas pembantu untuk menjalankan atau memanggil bagian dari kode Java :-)

Ini didasarkan pada jawaban yang sangat baik dari 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;
    }

}
Pascal
sumber
2

Cuplikan berikut akan memulai operasi di utas terpisah, lalu tunggu hingga 10 detik hingga operasi selesai. Jika operasi tidak selesai dalam waktu, kode akan berusaha untuk membatalkan operasi, kemudian melanjutkan dengan cara yang menyenangkan. Bahkan jika operasi tidak dapat dibatalkan dengan mudah, utas induk tidak akan menunggu utas anak berakhir.

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
}

The getExecutorService()Metode dapat diimplementasikan dalam beberapa cara. Jika Anda tidak memiliki persyaratan khusus, Anda dapat memanggil Executors.newCachedThreadPool()penggabungan utas tanpa batas atas jumlah utas.

markusk
sumber
Apa saja impor yang diperlukan? Apa SomeClassdan Future?
ADTC
2

Satu hal yang belum pernah saya lihat disebutkan adalah bahwa membunuh utas umumnya adalah Ide Buruk. Ada beberapa teknik untuk membuat metode berulir dapat dibatalkan dengan bersih , tetapi itu berbeda dengan hanya mematikan utas setelah waktu habis.

Risiko dengan apa yang Anda sarankan adalah bahwa Anda mungkin tidak tahu bagaimana keadaan utas saat Anda membunuhnya - jadi Anda berisiko menimbulkan ketidakstabilan. Solusi yang lebih baik adalah memastikan kode ulir Anda tidak hang sendiri, atau akan merespons permintaan abort dengan baik.

Dan Puzey
sumber
Tanpa konteks, pernyataan seperti milik Anda terdengar terlalu membatasi. Dalam lingkungan akademik, saya sering perlu menguji sesuatu hingga batas waktu, dan ketika itu terjadi, saya cukup menghapus semua perhitungan dan mencatat bahwa batas waktu itu terjadi. Mungkin jarang dalam industri ini, tetapi masih ...
Alessandro S.
@AlessandroS: itu poin yang masuk akal, meskipun OP meminta "solusi yang lebih baik," yang saya maksudkan bahwa ketahanan dan keandalan lebih disukai daripada kekerasan.
Dan Puzey
2

Jawaban hebat dari BalusC:

tetapi Hanya untuk menambahkan bahwa batas waktu itu sendiri tidak mengganggu utas itu sendiri. bahkan jika Anda memeriksa while (! Thread.interrupted ()) dalam tugas Anda. jika Anda ingin memastikan utas dihentikan, Anda juga harus memastikan future.cancel () dipanggil saat pengecualian batas waktu habis.

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

Saya pikir jawabannya terutama tergantung pada tugas itu sendiri.

  • Apakah itu melakukan satu tugas berulang-ulang?
  • Apakah perlu bahwa batas waktu menyela tugas yang sedang berjalan segera setelah berakhir?

Jika jawaban pertama adalah ya dan jawaban kedua adalah tidak, Anda dapat membuatnya sesederhana ini:

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

Jika ini bukan opsi, persempit persyaratan Anda - atau tunjukkan beberapa kode.

sfussenegger
sumber
0

Saya sedang mencari ExecutorService yang dapat mengganggu semua waktu habis Runnables dieksekusi olehnya, tetapi tidak menemukannya. Setelah beberapa jam saya membuat satu seperti di bawah ini. Kelas ini dapat dimodifikasi untuk meningkatkan ketahanan.

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

Pemakaian:

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

Sekarang, saya menemui masalah seperti ini. Itu terjadi pada decode gambar. Proses decode memakan waktu terlalu lama sehingga layar tetap hitam. l menambahkan pengontrol waktu: ketika waktu terlalu lama, lalu muncul dari Utas saat ini. Berikut ini perbedaannya:

   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);
Liu Jing
sumber
0

Saya memiliki masalah yang sama. Jadi saya datang dengan solusi sederhana seperti ini.

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

Menjamin bahwa jika blok tidak dieksekusi dalam batas waktu. proses akan mengakhiri dan melempar pengecualian.

contoh:

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
    }
Niroshan Abeywickrama
sumber
0

Dalam solusi yang diberikan oleh BalusC , utas utama akan tetap diblokir untuk periode waktu habis. Jika Anda memiliki kumpulan utas dengan lebih dari satu utas, Anda akan membutuhkan jumlah utas tambahan yang sama yang akan menggunakan Future.get (waktu habis, unit TimeUnit) memblokir panggilan untuk menunggu dan menutup utas jika melebihi periode waktu habis.

Solusi umum untuk masalah ini adalah membuat Dekorator ThreadPoolExecutor yang dapat menambahkan fungsionalitas batas waktu. Kelas Dekorator ini harus membuat utas sebanyak yang dimiliki ThreadPoolExecutor, dan semua utas ini hanya digunakan untuk menunggu dan menutup ThreadPoolExecutor.

Kelas generik harus diimplementasikan seperti di bawah ini:

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

Dekorator di atas dapat digunakan sebagai berikut:

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

    }
}
Sumeet Sahu
sumber