Java'da InterruptedException'ı kullanma


317

Aşağıdaki kullanım şekilleri arasındaki fark nedir InterruptedException? Bunu yapmanın en iyi yolu ne?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

VEYA

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

EDIT: Bu senaryoların hangi senaryolarda kullanıldığını da bilmek istiyorum.


Yanıtlar:


436

Aşağıdaki InterruptedException'ı kullanma yolları arasındaki fark nedir? Bunu yapmanın en iyi yolu ne?

Muhtemelen bu soruyu sormaya geldiniz çünkü atan bir yöntem çağırdınız InterruptedException.

Her şeyden önce, throws InterruptedExceptionne olduğunu görmelisiniz : Yöntem imzasının bir parçası ve çağırdığınız yöntemi çağırmanın olası bir sonucu. Öyleyse InterruptedException, bir yöntem çağrısının mükemmel geçerli bir sonucu olduğu gerçeğini benimseyerek başlayın .

Şimdi, çağırdığınız yöntem böyle bir istisna atarsa , yönteminiz ne yapmalıdır ? Cevabı aşağıdakileri düşünerek anlayabilirsiniz:

O yöntemi için mantıklı mı sen bir atmaya uyguladıklarını InterruptedException? Diğer bir deyişle, bir olduğunu InterruptedExceptionçağrılırken mantıklı bir sonuç sizin yöntemi?

  • Eğer evet , o zaman throws InterruptedExceptionbir parçası olmalıdır senin yöntem imzası ve siz (yani hiç yakalamak değil) istisna yaymak izin vermeli.

    Örnek : Yönteminiz, hesaplamayı bitirmek ve bir sonuç döndürmek için ağdan bir değer bekler. Engelleme şebeke çağrısı atarsa InterruptedException, yönteminiz hesaplamayı normal bir şekilde bitiremez. InterruptedExceptionYayılmasına izin verdin .

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
  • Eğer hayır ise , yönteminizi beyan throws InterruptedExceptionetmemelisiniz ve istisnayı yakalamanız gerekir. Şimdi bu durumda akılda tutulması gereken iki şey var:

    1. Birisi iş parçacığına ara verdi. Birisi muhtemelen işlemi iptal etmek, programı zarif bir şekilde sonlandırmak ya da her neyse. Birisine karşı nazik olmalısınız ve daha fazla uzatmadan yönteminizden dönmelisiniz.

    2. Olsa da yöntem durumunda mantıklı dönüş değeri üretmek için yönetebilir InterruptedExceptioniplik hala önemli olabilir kesildi gerçeği. Özellikle, yönteminizi çağıran kod, yönteminizin yürütülmesi sırasında bir kesinti olup olmadığıyla ilgilenebilir. Bu nedenle, kesilen bayrağı ayarlayarak bir kesinti gerçekleştiğini günlüğe kaydetmelisiniz:Thread.currentThread().interrupt()

    Örnek : Kullanıcı toplam iki değerin yazdırılmasını istedi. Toplam " Failed to compute sumhesaplanamıyorsa " " yazdırma işlemi kabul edilebilir (ve programın bir nedeniyle bir yığın izlemesiyle çökmesine izin vermekten çok daha iyi InterruptedException). Başka bir deyişle, bu yöntemi beyan etmek mantıklı değildirthrows InterruptedException .

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }

Şimdiye kadar sadece yapmanın throw new RuntimeException(e)kötü bir fikir olduğu açık olmalıdır . Arayan için çok kibar değil. Yeni bir çalışma zamanı istisnası icat edebilirsiniz, ancak kök neden (birisi iş parçacığının yürütmeyi durdurmasını istiyor) kaybolabilir.

Diğer örnekler:

UygulamaRunnable : Runnable.runKeşfetmiş olabileceğiniz gibi, imzası yeniden incelemeye izin vermez InterruptedExceptions. Eh, sen uygulanmasına kaydolmuş Runnableolduğunu hangi vasıta, sen mümkün olan anlaşma kaydoldum InterruptedExceptions. Ya farklı bir arayüz seçin Callableya da yukarıdaki ikinci yaklaşımı izleyin.

 

Sesli aramaThread.sleep : Bir dosyayı okumaya çalışıyorsunuz ve teknik özellik arasında 1 saniye ile 10 kez denemeniz gerektiğini söylüyor. Sen çağır Thread.sleep(1000). Yani, uğraşmanız gerekiyor InterruptedException. Gibi bir yöntem için tryToReadFileo demek çok mantıklı "Ben kesintiye ediyorum, ben dosyayı okumaya çalışıyorum benim eylemi tamamlayamıyor" . Başka bir deyişle, yöntemin atması mükemmel bir mantıklı InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

Bu yazı burada bir makale olarak yeniden yazılmıştır .


İkinci yöntemin sorunu nedir?
blitzkriegz

4
Makale interrupt(), kesilen durumu korumak için aramanız gerektiğini söylüyor . Bunu bir a'da yapmamanın nedeni nedir Thread.sleep()?
oksayt

7
İş parçacığınızın kesmeleri desteklememesi durumunda, alınan kesintiler beklenmedik bir durumu (yani bir programlama hatası, kötü API kullanımı) gösterir ve bir RuntimeException ile kurtarmanın uygun bir yanıt (başarısız-hızlı) olduğunu kabul etmiyorum ). Kesintileri desteklemeseniz bile bunları aldığınızda çalışmaya devam etmeniz gereken bir iş parçacığının genel sözleşmesinin bir parçası olmadığı sürece.
Ağustos'ta amoe

12
InterruptedExceptions'ı atan ve "kesintileri desteklemediğinizi" söyleyen çağrı yöntemleri özensiz programlama ve gerçekleşmeyi bekleyen bir hata gibi görünüyor. Farklı şekilde koy; Eğer InterruptedException atmak bildirilmiş bir yöntemi çağırmak Eğer gerektiği değil bu yöntem aslında böyle istisna eğer beklenmedik bir durum olabilir.
aioobe

1
@MartiNito, hayır, Thread.currentThread.interrupt()bir InterruptedExceptioncatch bloğunda çağrı yapmak sadece kesme bayrağını ayarlayacaktır. Başka InterruptedExceptionbir InterruptedExceptionyakalama bloğuna başka birinin atılmasına / yakalanmasına neden olmaz .
aioobe

97

Aslında bu sabah Brian Goetz'in Java Concurrency In Practice'de çalışmaya giderken bunu okuyordum . Temelde üç şeyden birini yapmanız gerektiğini söylüyor

  1. YaymakInterruptedException - InterruptedExceptionArayan ile başa çıkmak için kontrol atmak için yönteminizi bildirin .

  2. Kesmeyi Geri Yükle - Bazen atamazsınız InterruptedException. Bu durumlarda yakalamak gerektiğini InterruptedExceptionve arayarak kesme durumunu geri interrupt()üzerindeki yöntemi currentThreadkodu daha yüksek yukarı böylece çağrı yığını bir kesme verildiğini görebilirsiniz ve hızlı bir şekilde yöntemden dönün. Not: Bu yalnızca yönteminizde "deneme" veya "en iyi çaba" anlambilimi varsa geçerlidir; yani, yöntem hedefine ulaşmazsa kritik bir şey olmaz. Örneğin, log()ya da sendMetric()bu tür bir yöntem ya da olabilir boolean tryTransferMoney(), ancak void transferMoney(). Daha fazla ayrıntı için buraya bakın.

  3. Yöntem içindeki kesintiyi yok sayın, ancak çıkıştan sonra durumu geri yükleyin - örneğin, Guava üzerinden Uninterruptibles. UninterruptiblesJCIP § 7.1.3'teki Kâr Edilemeyen Görev örneğindeki gibi kaynak plakası kodunu devralın.

10
"Bazen InterruptedException alamazsınız" - söyleyebilirim, bazen InterruptedExceptions propagaet yöntemi için uygun değildir . Sizin formülasyonu ne zaman sen InterruptedException rethrow gerektiğini önermek gibi görünüyor olabilir .
aioobe

1
Bölüm 7'yi okumanız gerekiyorsa, listelenmeyen bir görevin kesmeyi hemen geri yüklemediği ancak ancak bittikten sonra, Listeleme 7.7'deki gibi bazı incelikler olduğunu göreceksiniz . Ayrıca Goetz'in bu kitaba birçok ortak yazarı var ...
Fizz

1
Cevabınızı seviyorum, çünkü kısa, ancak ikinci durumda işlevinizden yavaşça ve hızlı bir şekilde geri dönmenin de belirtilmesi gerektiğine inanıyorum. Ortada Thread.sleep () olan uzun (veya sonsuz) bir döngü düşünün. InterruptedException yakalamak (). Kesme () Thread.currentThread çağırmalıdır ve döngünün patlak.
Yann Vo

@YannVo Önerinizi uygulamak için cevabı düzenledim.
leventov

18

Ne yapmaya çalışıyorsun?

InterruptedExceptionBir iş parçacığı bekleyen veya uyku ve başka bir iş parçacığı kesmeler o kullanırken atılır interruptsınıfta yöntemi Thread. Bu istisnayı yakalarsanız, iş parçacığının kesintiye uğradığı anlamına gelir. İş Thread.currentThread().interrupt();parçacığının "kesilmiş" durumunu başka bir yerden kontrol etmek istemiyorsanız, genellikle tekrar arama yapmanın bir anlamı yoktur .

Diğer fırlatma seçeneğinizle ilgili olarak, RuntimeExceptionyapmak çok akıllıca bir şey gibi görünmüyor (bunu kim yakalayacak? Nasıl ele alınacak?) Ama ek bilgi olmadan daha fazlasını söylemek zor.


14
Calling Thread.currentThread().interrupt()kesilen bayrağı (tekrar) belirler; bu, kesmenin daha yüksek bir seviyede fark edilmesini ve işlenmesini sağlamak istiyorsak gerçekten yararlıdır.
Péter Török

1
@ Péter: Bundan bahsetmek için sadece cevabı güncelliyordum. Teşekkürler.
Grodriguez

12

Benim için bununla ilgili en önemli şey: InterruptedException yanlış giden bir şey değil, size söylediklerinizi yapan iş parçacığı. Bu nedenle bir RuntimeException içinde sarılmış olarak yeniden yazmak sıfır mantıklıdır.

Birçok durumda, burada neyin yanlış gittiğini bilmiyorum ve düzeltmek için hiçbir şey yapamam, sadece mevcut işlem akışından çıkmasını istiyorum, bir RuntimeException içine sarılmış bir istisna yeniden düşünmek mantıklı ve uygulama genelinde istisna işleyicisine dokunarak giriş yapabilirim. Bir InterruptedException için durum böyle değil, sadece interrupt () çağrısına yanıt veren iş parçacığı, iş parçacığının işlenmesini zamanında iptal etmesine yardımcı olmak için InterruptedException özel durumunu atıyor.

Bu yüzden InterruptedException'ı yayın veya akıllıca yiyin (yani, ne yapmak istediklerini başaracağı bir yerde) ve kesme bayrağını sıfırlayın. InterruptedException atıldığında kesme bayrağının silineceğini unutmayın; Jdk kütüphanesi geliştiricilerinin yaptığı varsayım, istisnanın yakalanmasının onu işlemek için yeterli olduğudur, bu nedenle varsayılan olarak bayrak temizlenir.

Bu yüzden kesinlikle ilk yol daha iyidir, iş parçacığının gerçekten kesintiye uğramasını beklemiyorsanız ve bunu kesmek bir hataya neden olmazsa, sorudaki ikinci yayınlanan örnek yararlı değildir.

İşte bir örnekle kesintilerin nasıl çalıştığını açıklayan bir cevap . Örnek kodda, Runnable'ın çalıştırma yönteminde bir while döngüsünden kurtarmak için InterruptedException özelliğinin kullanıldığı kodu görebilirsiniz.


10

Doğru varsayılan seçim, atış listenize InterruptedException eklemektir. Bir Kesme başka bir iş parçacığının iş parçacığının bitmesini istediğini gösterir. Bu isteğin nedeni açıklığa kavuşturulamamıştır ve tamamen bağlamsaldır, bu nedenle başka bir bilginiz yoksa, bunun sadece kolay bir kapatma olduğunu varsaymanız gerekir ve kapatmayı önleyen her şey kolay olmayan bir yanıttır.

Java rastgele InterruptedException'ın atmayacak, tüm tavsiyeler uygulamanızı etkilemeyecektir, ancak geliştiricinin "yutmak" stratejisini takip etmesinin çok rahatsız edici olduğu bir durumla karşılaştım. Bir takım büyük bir test seti geliştirdi ve Thread kullandı. Şimdi testleri CI sunucumuzda yapmaya başladık ve bazen koddaki hatalar nedeniyle kalıcı beklemelere yapışacaktık. Durumu daha da kötüleştirmek için, CI işini iptal etmeye çalışırken hiçbir zaman kapanmadı çünkü testi iptal etmek için tasarlanan Kesinti işi iptal etmedi. Kutuya giriş yapmak ve süreçleri manuel olarak öldürmek zorunda kaldık.

Uzun lafın kısası, sadece InterruptedException özel durumunu atarsanız, iş parçacığınızın sona ermesi gereken varsayılan niyetle eşleşirsiniz. Eğer atma listenize InterruptedException ekleyemezseniz, bir RuntimeException içine kaydırın.

InterruptedException'ın RuntimeException'ın kendisi olması gerektiğine dair çok rasyonel bir argüman vardır, çünkü bu daha iyi bir "varsayılan" işlemeyi teşvik eder. Bu sadece bir RuntimeException değildir, çünkü tasarımcılar kategorik bir kurala RuntimeException kodunuzda bir hatayı temsil etmelidir. Bir InterruptedException doğrudan kodunuzdaki bir hatadan kaynaklanmadığı için değil. Ancak gerçek şu ki, kodunuzda bir hata olduğu için (yani sonsuz döngü, ölü kilit) ve Interrupt bu hatayı ele almak için başka bir iş parçacığının yöntemidir.

Yapılacak rasyonel temizlik olduğunu biliyorsanız, yapın. Kesinti için daha derin bir neden biliyorsanız, daha kapsamlı bir işlem yapabilirsiniz.

Özet olarak, ele alma tercihleriniz bu listeye uymalıdır:

  1. Varsayılan olarak, atışlara ekleyin.
  2. Atmalara eklenmesine izin verilmezse, RuntimeException (e) öğesini atın. (Çoklu kötü seçeneklerin en iyi seçimi)
  3. Yalnızca Kesme'nin açık bir nedenini biliyorsanız, istediğiniz gibi işleyin. İşleme yönteminiz için yerel ise, Thread.currentThread (). İnterrupt () çağrısı ile kesildi.

3

Çoğu insanın ve makalenin bahsettiklerine son bir seçenek eklemek istedim. MR_fr0g'ın belirttiği gibi, kesmenin aşağıdaki yollarla doğru bir şekilde ele alınması önemlidir:

  • InterruptException özel durumu yayılıyor

  • İş Parçasındaki Kesme durumunu geri yükle

Veya ek olarak:

  • Kesmenin özel kullanımı

Koşullara bağlı olarak kesmenin özel bir şekilde ele alınmasında yanlış bir şey yoktur. Bir kesinti sonlandırma isteği olduğundan, zorlayıcı bir komutun aksine, uygulamanın isteği zarif bir şekilde işlemesine izin vermek için ek işleri tamamlamak mükemmel bir şekilde geçerlidir. Örneğin, bir İş parçacığı Uyku durumundaysa, IO veya bir donanım yanıtı beklerse, Kesinti aldığında, iş parçacığını sonlandırmadan önce herhangi bir bağlantıyı zarif bir şekilde kapatmak mükemmel şekilde geçerlidir.

Konuyu anlamayı şiddetle tavsiye ediyorum, ancak bu makale iyi bir bilgi kaynağı: http://www.ibm.com/developerworks/java/library/j-jtp05236/


0

Bazı durumlarda hiçbir şey yapmamın iyi olacağını söyleyebilirim. Muhtemelen varsayılan olarak yapmanız gereken bir şey değil, ancak kesmenin gerçekleşmesi için bir yol olmaması durumunda, başka ne yapacağından emin değilim (muhtemelen günlük hatası, ancak bu program akışını etkilemez).

Bir durum, bir görev (engelleme) kuyruğunuz olması durumunda olabilir. Bu görevleri yürüten bir daemon Thread'iniz varsa ve Thread'i kendiniz kesmezseniz (bildiklerime göre jvm jvm kapatma sırasında daemon ipliklerini kesmez), kesmenin gerçekleşmesi için bir yol görmüyorum ve bu nedenle görmezden geldi. (Ben bir daemon thread jvm tarafından herhangi bir zamanda öldürülebileceğini biliyorum ve bu nedenle bazı durumlarda uygun değildir).

DÜZENLEME: Başka bir vaka, en azından Oracle'ın http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html adresindeki öğreticisine dayanarak korunan bloklar olabilir.


Bu kötü bir tavsiye. Kesintileri görmezden gelmek için çok önemli olan herhangi bir görevi düşünmede aşırı sorun yaşıyorum. Oracle muhtemelen tembeldi ve Thread.sleep () çağrısına (daha fazla) karmaşa eklemek istemedi. İş parçacığınızın neden kesintiye uğradığını bilmiyorsanız, yaptığınız her şeyi durdurmalısınız (iş parçacığınızın olabildiğince hızlı ölmesine yardımcı olmak için geri dönün veya bir istisna oluşturun).
Ajax

Bu yüzden bazen dedim. Ayrıca henüz önerilmedi ve bazı durumlarda benim görüşüme göre gayet iyi çünkü . Sanırım ne tür bir yazılım geliştirdiğinize bağlı, ancak hiç durmaması gereken (7/24 kapanma dışında) 24/7 çalışan bir iş parçanız varsa, kesintileri örneğin bir BlockingQueue'dan bir öğeyi hata olarak almam (yani log) çünkü "olmamalı" ve tekrar deneyin. Tabii ki program sonlandırmasını kontrol etmek için ayrı bayraklar olurdu. Bazı programlarımda JVM'nin kapatılması normal çalışma altında olması gereken bir şey değildir.
Bjarne Boström

Ya da başka bir deyişle: Bence fazladan bir hata günlüğü ifadesi (ve örneğin hata günlüklerine posta gönderilmesi), kesinti nedeniyle durmak için 7/24 çalışması gereken bir uygulamaya sahip olmaktan daha iyidir. Normal koşullar altında gerçekleşmenin bir yolunu göremiyorum.
Bjarne Boström

Hımm, kullanım durumlarınız farklı olabilir, ancak ideal bir dünyada, sadece sona erdirmezsiniz çünkü kesintiye uğramışsınızdır. Kuyruktan işi almayı bırakır ve bu iş parçacığının ölmesine izin verirsiniz. İş parçacığı zamanlayıcısı da kesilir ve kapatılması söylenirse, JVM sona erer ve oyunu sona erer. Özellikle, kill -9 gönderirseniz veya posta sunucunuzu veya herhangi bir şeyi başka bir şekilde devirmeye çalışıyorsanız, çıkmaya zorlayana kadar sizi görmezden gelir ve böylece kodunuzun diğer bölümlerinin zarif kapanmasını önler. Diske veya db'ye yazarken öldürmeye zorla ve eminim harika şeyler olacak.
Ajax
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.