Java'da zaman aşımına sahip bazı engelleme yöntemlerini nasıl çağırırım?


97

Java'da zaman aşımı olan bir engelleme yöntemini çağırmanın standart güzel bir yolu var mı? Yapabilmek istiyorum:

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

Mantıklı geliyorsa.

Teşekkürler.


1
Referans olarak, Brian Goetz s. 126 - 134, özellikle 6.3.7 "Görevlere zaman sınırları
koyma

Yanıtlar:


155

Bir İnfazcı kullanabilirsiniz:

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
}

Eğer future.get5 saniye içinde dönmez, bir atar TimeoutException. Zaman aşımı saniye, dakika, milisaniye veya sabit olarak mevcut herhangi bir birim olarak yapılandırılabilir TimeUnit.

Daha fazla ayrıntı için JavaDoc'a bakın .


13
Engelleme yöntemi zaman aşımından sonra bile çalışmaya devam edecek, değil mi?
Ivan Dubrov

1
Bu, geleceğe bağlıdır. İptal. Engelleme yönteminin o anda ne yaptığına bağlı olarak, sona erebilir veya bitmeyebilir.
skaffman

4
parametreyi blockingMethod () 'a nasıl iletebilirim? Teşekkürler!
Robert A Henru

@RobertAHenru: Yapılandırıcısı BlockingMethodCallablegeçmek istediğiniz parametreleri kabul eden blockingMethod()ve bunları üye değişkenler (muhtemelen son olarak) olarak depolayan adında yeni bir sınıf oluşturun . Sonra içeride call()bu parametreleri blockMethod().
Vite Falcon

1
son olarak yapmalı future.cancel(true)- Future <Object> türündeki cancel (boolean) yöntemi arguments () için geçerli değil
Noam Manos



4

İnsanların bunu pek çok şekilde uygulamaya çalışması gerçekten harika. Ama gerçek şu ki, hiçbir yolu yok.

Çoğu geliştirici, engelleme çağrısını farklı bir iş parçacığına koymaya ve bir geleceğe veya zamanlayıcıya sahip olmaya çalışır. ANCAK, iş parçacığı kesintisini açıkça işleyen Thread.sleep () ve Lock.lockInterruptously () yöntemleri gibi birkaç özel durum bir yana, Java'da bir iş parçacığını harici olarak durdurmanın bir yolu yoktur.

Yani gerçekten sadece 3 genel seçeneğiniz var:

  1. Engelleme aramanızı yeni bir iş parçacığına koyun ve süre sona ererse, o ileti dizisini asılı bırakarak devam edin. Bu durumda, iş parçacığının bir Daemon iş parçacığı olacak şekilde ayarlandığından emin olmalısınız. Bu şekilde iş parçacığı uygulamanızın sonlandırılmasını durdurmaz.

  2. Engellemeyen Java API'leri kullanın. Dolayısıyla, örneğin ağ için, NIO2'yi kullanın ve engellemeyen yöntemleri kullanın. Konsoldan okumak için, engellemeden önce Scanner.hasNext () öğesini kullanın.

  3. Engelleme aramanız bir GÇ değilse, ancak mantığınızsa, Thread.isInterrupted()harici olarak kesintiye uğrayıp uğramadığını tekrar tekrar kontrol edebilir thread.interrupt()ve engelleme iş parçacığı üzerinde başka bir iş parçacığı çağrısı yapabilirsiniz .

Eşzamanlılık ile ilgili bu kurs https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

Java'da nasıl çalıştığını gerçekten anlamak istiyorsanız, gerçekten bu temellerin üzerinden geçer. Aslında bu belirli sınırlamalar ve senaryolar hakkında ve derslerden birinde bunlardan nasıl bahsedileceği hakkında konuşuyor.

Mümkün olduğunca engelleme çağrıları kullanmadan kişisel olarak programlamaya çalışıyorum. Örneğin Vert.x gibi, IO işlemlerini eşzamansız olarak ve engellemesiz bir şekilde gerçekleştirmeyi gerçekten kolaylaştıran ve verimli kılan araç setleri vardır.

Umut ediyorum bu yardım eder


3

Bunun için jcabi-noktaları kitaplığı ile bir AspectJ çözümü de vardır .

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

Daha kısa ve öz olamaz, ancak AspectJ'ye güvenmeniz ve tabii ki bunu geliştirme yaşam döngünüzde tanıtmanız gerekir.

Bunu daha ayrıntılı olarak açıklayan bir makale var: Java Yöntemi Yürütme Süresini Sınırlayın


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

Bu durdurmanın kullanımdan kaldırıldığını unutmayın, daha iyi bir alternatif, blockingMethod () içinde bazı geçici boole bayrakları ayarlamaktır, bunu kontrol edin ve şu şekilde çıkın:

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

1

Bunu dene. Daha basit çözüm. Blok zaman sınırı içinde çalışmazsa bunu garanti eder. süreç sona erecek ve bir istisna atacaktır.

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

misal :

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
    }

1

Size burada tam kodu veriyorum. Aradığım yöntemin yerine, yönteminizi kullanabilirsiniz:

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

0

blockingMethodBirkaç milisaniye uyuduğunu varsayalım :

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

Benim çözümüm kullanmak wait()ve bunun synchronizedgibi:

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

Böylece değiştirebilirsin

something.blockingMethod(input)

-e

something.blockingMethod(input, 2000)

Umarım yardımcı olur.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.