Bagaimana cara memanggil beberapa metode pemblokiran dengan waktu tunggu di Java?

97

Apakah ada cara standar yang bagus untuk memanggil metode pemblokiran dengan waktu tunggu di Java? Saya ingin bisa melakukan:

// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it

jika itu masuk akal.

Terima kasih.

jjujuma
sumber
1
Sebagai referensi, lihat Java Concurrency in Practice oleh Brian Goetz hlm. 126 - 134, khususnya bagian 6.3.7 "Menempatkan batas waktu pada tugas"
brown.2179

Jawaban:

152

Anda bisa menggunakan Pelaksana:

ExecutorService executor = Executors.newCachedThreadPool();
Callable<Object> task = new Callable<Object>() {
   public Object call() {
      return something.blockingMethod();
   }
};
Future<Object> future = executor.submit(task);
try {
   Object result = future.get(5, TimeUnit.SECONDS); 
} catch (TimeoutException ex) {
   // handle the timeout
} catch (InterruptedException e) {
   // handle the interrupts
} catch (ExecutionException e) {
   // handle other exceptions
} finally {
   future.cancel(true); // may or may not desire this
}

Jika future.gettidak kembali dalam 5 detik, itu melempar TimeoutException. Batas waktu dapat dikonfigurasi dalam detik, menit, milidetik atau unit apa pun yang tersedia sebagai konstanta dalam TimeUnit.

Lihat JavaDoc untuk detail selengkapnya.

skaffman
sumber
13
Metode pemblokiran akan terus berjalan bahkan setelah batas waktu, bukan?
Ivan Dubrov
1
Itu tergantung pada masa depan. Batalkan. Bergantung pada apa yang dilakukan metode pemblokiran pada saat itu, itu mungkin atau mungkin tidak berhenti.
Skaffman
4
bagaimana cara mengirimkan parameter ke blockingMethod ()? Terima kasih!
Robert A Henru
@RobertAHenru: Buat kelas baru yang disebut BlockingMethodCallablepembuatnya menerima parameter yang ingin Anda teruskan blockingMethod()dan menyimpannya sebagai variabel anggota (mungkin sebagai variabel final). Kemudian di dalam, call()berikan parameter tersebut ke blockMethod().
Vite Falcon
1
akhirnya harus dilakukan future.cancel(true)- Metode batal (boolean) dalam tipe Future <Object> tidak berlaku untuk argumen ()
Noam Manos
9

Anda bisa menggabungkan panggilan dalam a FutureTaskdan menggunakan versi waktu tunggu dari get ().

Lihat http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html

Colin Goudie
sumber
1
FutureTask sendiri tidak asinkron, bukan? Dengan sendirinya, ia hanya melakukan berbagai hal secara sinkron, Anda perlu menggabungkannya dengan Pelaksana untuk melakukan perilaku asinkron.
Skaffman
6

Lihat juga TimeLimiter Guava yang menggunakan Executor di belakang layar.

Federico
sumber
1
Ide bagus. Tapi tautannya sudah basi. Googlecode tidak lagi .. coba tautan ini ke SimpleTimeLimiter
Rhubarb
4

Sungguh luar biasa bahwa orang mencoba menerapkan ini dengan banyak cara. Tapi kenyataannya, TIDAK ADA cara.

Sebagian besar pengembang akan mencoba melakukan panggilan pemblokiran di utas yang berbeda dan memiliki masa depan atau pengatur waktu. TETAPI tidak ada cara di Java untuk menghentikan utas secara eksternal, apalagi beberapa kasus yang sangat spesifik seperti metode Thread.sleep () dan Lock.lockInterruptibly () yang secara eksplisit menangani interupsi utas.

Jadi sebenarnya Anda hanya memiliki 3 opsi umum:

  1. Letakkan panggilan pemblokiran Anda di utas baru dan jika waktunya habis, lanjutkan saja, biarkan utas itu menggantung. Dalam hal ini Anda harus memastikan utas disetel menjadi utas Daemon. Dengan cara ini utas tidak akan menghentikan aplikasi Anda untuk berhenti.

  2. Gunakan API Java yang tidak memblokir. Jadi untuk jaringan misalnya, gunakan NIO2 dan gunakan metode non-blocking. Untuk membaca dari konsol gunakan Scanner.hasNext () sebelum memblokir dll.

  3. Jika panggilan pemblokiran Anda bukan IO, tetapi logika Anda, Anda dapat berulang kali memeriksa untuk Thread.isInterrupted()memeriksa apakah panggilan itu terputus secara eksternal, dan melakukan panggilan utas lain di utas thread.interrupt()pemblokiran

Kursus tentang konkurensi ini https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

benar-benar mempelajari dasar-dasar tersebut jika Anda benar-benar ingin memahami cara kerjanya di Java. Ini benar-benar berbicara tentang batasan dan skenario tertentu, dan bagaimana melakukannya di salah satu kuliah.

Saya pribadi mencoba memprogram tanpa menggunakan pemblokiran panggilan sebanyak mungkin. Ada toolkit seperti Vert.x misalnya yang membuatnya sangat mudah dan berkinerja baik untuk melakukan IO dan tidak ada operasi IO secara asinkron dan dengan cara yang tidak memblokir.

Saya harap ini membantu

Michael P.
sumber
3

Ada juga solusi AspectJ untuk itu dengan library aspek jcabi .

@Timeable(limit = 30, unit = TimeUnit.MINUTES)
public Soup cookSoup() {
  // Cook soup, but for no more than 30 minutes (throw and exception if it takes any longer
}

Tidak bisa lebih ringkas, tetapi Anda harus bergantung pada AspectJ dan memperkenalkannya dalam siklus proses build Anda, tentunya.

Ada artikel yang menjelaskan lebih lanjut: Batasi Waktu Eksekusi Metode Java

gvlasov.dll
sumber
1
Thread thread = new Thread(new Runnable() {
    public void run() {
        something.blockingMethod();
    }
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
    thread.stop();
}

Perhatikan, perhentian itu tidak digunakan lagi, alternatif yang lebih baik adalah menyetel beberapa tanda boolean volatile, di dalam blockingMethod () periksa dan keluar, seperti ini:

import org.junit.*;
import java.util.*;
import junit.framework.TestCase;

public class ThreadTest extends TestCase {
    static class Something implements Runnable {
        private volatile boolean stopRequested;
        private final int steps;
        private final long waitPerStep;

        public Something(int steps, long waitPerStep) {
            this.steps = steps;
            this.waitPerStep = waitPerStep;
        }

        @Override
        public void run() {
            blockingMethod();
        }

        public void blockingMethod() {
            try {
                for (int i = 0; i < steps && !stopRequested; i++) {
                    doALittleBit();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        public void doALittleBit() throws InterruptedException {
            Thread.sleep(waitPerStep);
        }

        public void setStopRequested(boolean stopRequested) {
            this.stopRequested = stopRequested;
        }
    }

    @Test
    public void test() throws InterruptedException {
        final Something somethingRunnable = new Something(5, 1000);
        Thread thread = new Thread(somethingRunnable);
        thread.start();
        thread.join(2000);
        if (thread.isAlive()) {
            somethingRunnable.setStopRequested(true);
            thread.join(2000);
            assertFalse(thread.isAlive());
        } else {
            fail("Exptected to be alive (5 * 1000 > 2000)");
        }
    }
}
jnr
sumber
1

Coba ini. Solusi yang lebih sederhana. Menjamin jika blok tidak dieksekusi dalam batas waktu. proses ini akan berhenti dan memunculkan pengecualian.

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

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
1

Saya memberi Anda di sini kode lengkap. Sebagai ganti metode yang saya panggil, Anda dapat menggunakan metode Anda:

public class NewTimeout {
    public String simpleMethod() {
        return "simple method";
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        Callable<Object> task = new Callable<Object>() {
            public Object call() throws InterruptedException {
                Thread.sleep(1100);
                return new NewTimeout().simpleMethod();
            }
        };
        Future<Object> future = executor.submit(task);
        try {
            Object result = future.get(1, TimeUnit.SECONDS); 
            System.out.println(result);
        } catch (TimeoutException ex) {
            System.out.println("Timeout............Timeout...........");
        } catch (InterruptedException e) {
            // handle the interrupts
        } catch (ExecutionException e) {
            // handle other exceptions
        } finally {
            executor.shutdown(); // may or may not desire this
        }
    }
}
VickyCool
sumber
0

Asumsikan blockingMethodhanya tidur selama beberapa mili:

public void blockingMethod(Object input) {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Solusi saya adalah menggunakan wait()dan synchronizedseperti ini:

public void blockingMethod(final Object input, long millis) {
    final Object lock = new Object();
    new Thread(new Runnable() {

        @Override
        public void run() {
            blockingMethod(input);
            synchronized (lock) {
                lock.notify();
            }
        }
    }).start();
    synchronized (lock) {
        try {
            // Wait for specific millis and release the lock.
            // If blockingMethod is done during waiting time, it will wake
            // me up and give me the lock, and I will finish directly.
            // Otherwise, when the waiting time is over and the
            // blockingMethod is still
            // running, I will reacquire the lock and finish.
            lock.wait(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Jadi kamu bisa mengganti

something.blockingMethod(input)

untuk

something.blockingMethod(input, 2000)

Semoga membantu.

Euporie
sumber