ในที่สุดบล็อกจะทำงานเสมอหรือไม่?


112

มีเงื่อนไขใดบ้างที่ในที่สุดอาจไม่ทำงานใน java? ขอบคุณ


13
นี่เป็นคำถามที่คุณอาจถูกถามเมื่อพยายามหางานกับ บริษัท ที่มีชื่อเสียงหรือไม่?
Tom Hawtin - แท

@ TomHawtin-tackline สนใจตั้งชื่อไหม (พระเจ้าฉันคิดถึงโพสต์นี้เก่าแค่ไหน!)
Hele

6
@ เฮลฉันไม่ต้องการให้เกมนี้ไป แต่คุณสามารถ Google ได้
Tom Hawtin - แทคไลน์

คำตอบสั้น ๆ : ใช่ภายใต้สภาวะปกติ
Shark

คำตอบ:


139

จากบทแนะนำ Sun

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

ฉันไม่รู้วิธีอื่นใดที่สุดท้ายบล็อกจะไม่ดำเนินการ ...


6
@dhiller - ฉันค่อนข้างมั่นใจว่า "ปิดเครื่อง" รวมอยู่ใน "ถ้า JVM ออก ... " :-p
Jason Coco

2
@ Jason Coco: การยุติ (เมื่อสูญเสียอำนาจ) ไม่ใช่สิ่งเดียวกับการออก อย่างหลังเป็นกระบวนการที่มีการจัดระเบียบมากขึ้นหรือน้อยลงในอดีต ; p
user359996

2
AFAIK หากเธรดถูกขัดจังหวะจะไม่หยุดทันที มันขึ้นอยู่กับรหัสในเธรดเพื่อตรวจจับการขัดจังหวะและหยุดงานดังนั้นในที่สุดโค้ดก็ควรทำงาน
Bart van Heukelom

2
ฉันเดาว่าหากเกิดข้อยกเว้นในบล็อกสุดท้ายบล็อกที่เหลือจะไม่ดำเนินการ
Adriaan Koster

ดีจัง!
eiran

63

System.exitปิดเครื่องเสมือน

ยุติ Java Virtual Machine ที่กำลังรันอยู่ อาร์กิวเมนต์ทำหน้าที่เป็นรหัสสถานะ ตามแบบแผนรหัสสถานะที่ไม่ใช่ศูนย์แสดงถึงการยุติที่ผิดปกติ

วิธีการนี้เรียกว่าวิธีการในชั้นเรียนexit Runtimeวิธีนี้จะไม่คืนค่าตามปกติ

    try {
        System.out.println("hello");
        System.exit(0);
    }
    finally {
        System.out.println("bye");
    } // try-finally

"ลาก่อน" ไม่ได้พิมพ์ออกมาในโค้ดด้านบน


นอกจากนี้ข้อยกเว้นควรปิด Java Virtual Machine
kaissun

3
หากมีข้อยกเว้นเกิดขึ้นขณะเรียกใช้ System.exit (0) สุดท้ายบล็อกจะดำเนินการแม้ว่า
halil

50

เพียงเพื่อขยายความในสิ่งที่คนอื่นพูดอะไรก็ตามที่ไม่ก่อให้เกิดบางอย่างเช่นการออกจาก JVM จะทำให้เกิดการปิดกั้นในที่สุด ดังนั้นวิธีการต่อไปนี้:

public static int Stupid() {
  try {
    return 0;
  }
  finally {
    return 1;
  }
}

จะรวบรวมและส่งคืน 1 อย่างแปลกประหลาด


2
สิ่งนี้ทำให้ฉันสับสนมากสำหรับสองสามชั่วโมงสองสามสัปดาห์ที่ผ่านมา
nickf

3
ถือเป็นความคิดที่ไม่ดีที่จะคืนค่าจากบล็อกสุดท้าย ไม่ว่าจะส่งคืนจากบล็อกการลองเท่านั้นหรือกลับจากภายนอกการลอง / บล็อกในที่สุด IDE ส่วนใหญ่จะทำเครื่องหมายพร้อมคำเตือน
Ran Biron

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

2
ที่น่าสงสัยใน C # ไม่อนุญาตให้กลับจากบล็อกสุดท้าย
JMCF125

3
@RanBiron แน่นอน. เขาไม่ได้แนะนำให้กลับเข้าไปในบล็อกในที่สุดเขาก็แค่พยายามแสดงให้เห็นว่าแม้แต่คำสั่งส่งคืนก็ยังทำให้โค้ดในบล็อกดังกล่าวทำงานได้
Aquarelle

15

ที่เกี่ยวข้องกับ System.exit นอกจากนี้ยังมีความล้มเหลวบางประเภทที่การบล็อกในที่สุดอาจไม่ทำงาน หาก JVM หมดหน่วยความจำทั้งหมดก็อาจจะออกโดยไม่ต้องจับหรือเกิดขึ้นในที่สุด

โดยเฉพาะอย่างยิ่งฉันจำโครงการที่เราพยายามใช้อย่างโง่เขลา

catch (OutOfMemoryError oome) {
    // do stuff
}

สิ่งนี้ไม่ได้ผลเนื่องจาก JVM ไม่มีหน่วยความจำเหลือสำหรับการเรียกใช้บล็อก catch


เมื่อ OutOfMemoryError ถูกโยนออกไปมักจะมีหน่วยความจำเหลืออยู่มากมาย (เพื่อหยุด GC thrashing) อย่างไรก็ตามหากคุณจับมันซ้ำ ๆ คุณจะเห็นได้ชัดว่าจะกลับไปที่การฟาด GC
Tom Hawtin - แท

ฉันคิดว่าไม่ควรจับข้อยกเว้นที่ไม่ได้ตรวจสอบ!
Sergii Shevchyk

1
ฉันพยายามเข้าข้างตัวเองโดยใช้ jdk7 แต่มันจับข้อผิดพลาด OutOfMemory ได้!
Jaskey

10
try { for (;;); } finally { System.err.println("?"); }

ในกรณีนี้สุดท้ายจะไม่ดำเนินการ (เว้นแต่Thread.stopจะเรียกว่าเลิกใช้แล้วหรือเทียบเท่าพูดผ่านอินเทอร์เฟซเครื่องมือ)


หน้านี้อ้างว่าเกิดข้อผิดพลาด ThreadDeath และสแต็กคลายตัวตามปกติเมื่อเรียก Thread.stop () มีการจับว่าฉันหายไป? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

ฉันไม่คิดว่าจะมีการจับ บางทีคุณอาจจินตนาการถึงสิ่งที่จับไม่ได้ ถ้าเราใส่ในอย่างชัดเจนthrowแล้วfinallyบล็อกจะดำเนินการตามที่คาดไว้ try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - แท็

9

บทแนะนำของ The Sun ถูกยกมาอย่างไม่ถูกต้องในหัวข้อนี้

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

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

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

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


6

นอกจากนี้หากการหยุดชะงัก / การล็อกเกิดขึ้นภายในtryบล็อก

นี่คือรหัสที่แสดงให้เห็น:

public class DeadLocker {
    private static class SampleRunnable implements Runnable {
        private String threadId;
        private Object lock1;
        private Object lock2;

        public SampleRunnable(String threadId, Object lock1, Object lock2) {
            super();
            this.threadId = threadId;
            this.lock1 = lock1;
            this.lock2 = lock2;
        }

        @Override
        public void run() {
            try {
                synchronized (lock1) {
                    System.out.println(threadId + " inside lock1");
                    Thread.sleep(1000);
                    synchronized (lock2) {
                        System.out.println(threadId + " inside lock2");
                    }
                }
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }

    }

    public static void main(String[] args) throws Exception {
        Object ob1 = new Object();
        Object ob2 = new Object();
        Thread t1 = new Thread(new SampleRunnable("t1", ob1, ob2));
        Thread t2 = new Thread(new SampleRunnable("t2", ob2, ob1));
        t1.start();
        t2.start();
    }
}

รหัสนี้สร้างผลลัพธ์ต่อไปนี้:

t1 inside lock1
t2 inside lock1

และ "ในที่สุด" จะไม่ได้รับการพิมพ์


3
ในทางเทคนิคบล็อกการลองจะไม่ออกจากการบล็อกในที่สุดจึงไม่ควรมีโอกาสดำเนินการ อาจกล่าวได้เช่นเดียวกันสำหรับการวนซ้ำที่ไม่มีที่สิ้นสุด
Jeff Mercado

6

หาก JVM ออกในขณะที่กำลังดำเนินการ try หรือ catch โค้ดสุดท้ายบล็อกอาจไม่ทำงาน (ที่มา )

Normal Shutdown - เกิดขึ้นเมื่อเธรดที่ไม่ใช่ daemon ตัวสุดท้ายออกจากหรือเมื่อ Runtime.exit () ( ต้นทาง )

เมื่อเธรดออก JVM จะดำเนินการจัดเก็บเธรดที่รันอยู่และหากเธรดเดียวที่เหลือเป็นเธรด daemon เธรดจะเริ่มการปิดระบบตามลำดับ เมื่อ JVM หยุดทำงานเธรด daemon ที่เหลือจะถูกละทิ้งในที่สุดบล็อกจะไม่ถูกดำเนินการสแต็กจะไม่คลาย JVM เพียงแค่ออก เธรด Daemon ควรใช้กิจกรรมการประมวลผลเพียงเล็กน้อยสามารถละทิ้งได้อย่างปลอดภัยเมื่อใดก็ได้โดยไม่ต้องล้างข้อมูล โดยเฉพาะอย่างยิ่งการใช้เธรด daemon สำหรับงานที่อาจดำเนินการ I / O ประเภทใดก็ได้เป็นอันตราย เธรด Daemon ถูกบันทึกไว้อย่างดีที่สุดสำหรับงาน "การดูแลทำความสะอาด" เช่นเธรดพื้นหลังที่ลบรายการที่หมดอายุออกจากแคชในหน่วยความจำเป็นระยะ (ที่มา )

เธรดที่ไม่ใช่ daemon ตัวสุดท้ายออกจากตัวอย่าง:

public class TestDaemon {
    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("Is alive");
                    Thread.sleep(10);
                    // throw new RuntimeException();
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                System.out.println("This will never be executed.");
            }
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread daemon = new Thread(runnable);
        daemon.setDaemon(true);
        daemon.start();
        Thread.sleep(100);
        // daemon.stop();
        System.out.println("Last non-daemon thread exits.");
    }
}

เอาท์พุท:

Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive

1

ในกรณีต่อไปนี้สุดท้ายบล็อกจะไม่ถูกดำเนินการ: -

  • เมื่อSystem.exit(0)ถูกเรียกใช้จากtryบล็อก
  • เมื่อ JVM หมดหน่วยความจำ
  • เมื่อกระบวนการ java ของคุณถูกฆ่าอย่างมีประสิทธิภาพจาก task mgr หรือ console
  • สภาพการหยุดชะงักในtryบล็อก ของคุณ
  • เมื่อเครื่องของคุณปิดลงเนื่องจากไฟฟ้าดับ

นอกจากนี้ยังอาจมีกรณีอื่น ๆ อีกด้วยซึ่งสุดท้ายแล้วการบล็อกจะไม่ถูกดำเนินการ


1

มีสองวิธีในการหยุดการทำงานของโค้ดในที่สุด:
1. ใช้ System.exit ();
2. หากการควบคุมการดำเนินการไม่ถึงพยายามบล็อก
ดู:

public class Main
{
  public static void main (String[]args)
  {
    if(true){
        System.out.println("will exceute");
    }else{
        try{
            System.out.println("result = "+5/0);
        }catch(ArithmeticException e){
          System.out.println("will not exceute");
        }finally{
          System.out.println("will not exceute");  
        }
    }
  }
}

0

ฉันเจอกรณีที่เฉพาะเจาะจงมากของการบล็อกในที่สุดที่ไม่ได้ดำเนินการที่เกี่ยวข้องกับกรอบการเล่นโดยเฉพาะ

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

try {
    InputStream is = getInputStreamMethod();
    renderBinary(is, "out.zip");
catch (Exception e) {
    e.printStackTrace();
} finally {
    cleanUp();
}

บางทีเธรดอาจถูกยกเลิกหรือมีบางอย่างเมื่อเรียกใช้ renderBinary () ฉันสงสัยว่าสิ่งเดียวกันนี้จะเกิดขึ้นกับการเรียกใช้ render () อื่น ๆ แต่ฉันไม่ได้ตรวจสอบ

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


-1
//If ArithmeticException Occur Inner finally would not be executed
class Temp
{
    public static void main(String[] s)
    {
        try
        {
        int x = 10/s.length;
        System.out.println(x);
        try
            {
                int z[] = new int[s.length];
                z[10] = 1000;
            }catch(ArrayIndexOutOfBoundsException e)
            {
                System.out.println(e);
            }
         finally
        {
            System.out.println("Inner finally");
        }
        }
        catch(ArithmeticException e)
        {
            System.out.println(e);
        }
    finally 
    {
        System.out.println("Outer Finally"); 
    }

System.out.println("Remaining Code");   
}
}

ปรับปรุงการเยื้องและเพิ่มรายละเอียดบางอย่าง
ROMANIA_engineer

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