Khối cuối cùng có luôn chạy không?


112

Có điều kiện nào mà cuối cùng có thể không chạy trong java không? Cảm ơn.


13
Đây có phải là câu hỏi bạn có thể nhận được khi cố gắng xin việc tại một công ty nổi tiếng nào đó không?
Tom Hawtin - tackline

@ TomHawtin-tackline Bạn muốn đặt tên cho nó? (Chúa ơi, tôi bỏ lỡ bài đăng này cũ đến mức nào!)
Hele

6
@Hele Tôi sẽ không muốn cho trò chơi đi, nhưng bạn có thể google nó.
Tom Hawtin - tackline vào

câu trả lời ngắn gọn: có, trong điều kiện bình thường.
Shark

Câu trả lời:


139

từ Hướng dẫn Mặt trời

Lưu ý: Nếu JVM thoát ra trong khi mã thử hoặc bắt đang được thực thi, thì khối cuối cùng có thể không thực thi. Tương tự như vậy, nếu chuỗi thực thi mã try hoặc catch bị gián đoạn hoặc bị giết, khối cuối cùng có thể không thực thi mặc dù toàn bộ ứng dụng vẫn tiếp tục.

Tôi không biết có cách nào khác mà khối cuối cùng sẽ không thực thi ...


6
@dhiller - Tôi khá chắc chắn rằng "quyền lực xuống" được bao gồm trong "Nếu thoát JVM ..." :-p
Jason Coco

2
@Jason Coco: Chấm dứt (khi mất điện) không hoàn toàn giống như thoát ra; thứ sau là một quá trình có tổ chức ít nhiều mà đỉnh cao là quá trình trước đây. ; p
user359996.

2
AFAIK, nếu một chuỗi bị gián đoạn, nó không bị tạm dừng ngay lập tức. Nó phụ thuộc vào mã trong chuỗi để phát hiện sự gián đoạn và dừng tác vụ của nó, vì vậy cuối cùng mã sẽ chạy.
Bart van Heukelom

2
Tôi đoán rằng nếu một ngoại lệ được ném vào khối cuối cùng, phần còn lại của khối sẽ không được thực thi.
Adriaan Koster

tốt mà tệ!
eiran

63

System.exit tắt Máy ảo.

Chấm dứt Máy ảo Java hiện đang chạy. Đối số đóng vai trò như một mã trạng thái; theo quy ước, mã trạng thái khác không cho biết kết thúc bất thường.

Phương thức này gọi exit phương thức trong lớp Runtime. Phương thức này không bao giờ trả về bình thường.

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

"bye" không in ra trong mã trên.


Ngoài ra, một ngoại lệ nên tắt Máy ảo Java.
kaissun

3
Nếu một Ngoại lệ xảy ra trong khi thực thi System.exit (0), thì khối cuối cùng sẽ thực thi.
halil

50

Chỉ để mở rộng những gì những người khác đã nói, bất cứ điều gì không gây ra điều gì đó giống như việc thoát JVM sẽ phải chịu khối cuối cùng. Vì vậy, phương pháp sau:

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

kỳ lạ sẽ cả biên dịch và trả về 1.


2
điều này thực sự khiến tôi bối rối trong vài giờ trong vài tuần trở lại đây.
nickf

3
Việc trả về giá trị từ khối cuối cùng được coi là một ý tưởng tồi. Chỉ trả về từ khối thử hoặc trở lại từ bên ngoài khối thử / cuối cùng. Hầu hết các IDE sẽ đánh dấu điều này bằng một cảnh báo.
Ran Biron

1
@nickf Tôi thu thập rằng bạn không còn bối rối nữa. Bạn có thể giải thích thêm về cơ chế tại sao 1 được trả về chứ không phải bằng 0. Tôi chỉ có thể đoán rằng bộ nhớ (hoặc nó là một thanh ghi) lưu trữ giá trị trả về của hàm ban đầu giữ 0, bị ghi đè khi khối cuối cùng được thực thi .
Yaneeve

2
Điều đó thật kỳ lạ, trong C #, nó không được phép trả về từ một khối cuối cùng.
JMCF125

3
@RanBiron Tất nhiên. Anh ấy không thực sự khuyên bạn nên quay lại bên trong một khối cuối cùng, anh ấy chỉ cố gắng chứng minh rằng ngay cả một câu lệnh trả về vẫn sẽ khiến mã trong khối đó thực thi.
Aquarelle

15

Liên quan đến System.exit, cũng có một số loại lỗi nghiêm trọng trong đó khối cuối cùng có thể không thực thi. Nếu JVM hết bộ nhớ hoàn toàn, nó có thể thoát ra mà không bắt được hoặc cuối cùng xảy ra.

Cụ thể, tôi nhớ một dự án mà chúng tôi đã cố gắng sử dụng

catch (OutOfMemoryError oome) {
    // do stuff
}

Điều này không hoạt động vì JVM không còn bộ nhớ để thực hiện khối bắt.


Khi OutOfMemoryError được ném ra, thường sẽ có rất nhiều bộ nhớ còn lại (để ngăn chặn sự tấn công của GC). Tuy nhiên, nếu bạn bắt nó lặp đi lặp lại, bạn rõ ràng sẽ quay trở lại hành động GC.
Tom Hawtin - tackline

Tôi nghĩ người ta không nên bắt các trường hợp ngoại lệ không được kiểm tra!
Sergii Shevchyk

1
Tôi đã thử ở bên cạnh mình, sử dụng jdk7, nhưng nó đã bắt lỗi OutOfMemory!
Jaskey

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

Trong trường hợp đó, cuối cùng sẽ không thực thi (trừ khi cái không dùng nữa Thread.stopđược gọi, hoặc tương đương, chẳng hạn, thông qua giao diện công cụ).


Trang này tuyên bố rằng một lỗi ThreadDeath được tạo ra và ngăn xếp sẽ mở lại bình thường khi Thread.stop () được gọi. Có một nắm bắt mà tôi đang thiếu? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

Tôi không nghĩ rằng có một sự bắt kịp. Có lẽ ở đó bạn đang tưởng tượng một cú đánh bắt không có ở đó. Nếu chúng ta đặt một lệnh rõ ràng throw, thì finallykhối sẽ thực thi như mong đợi. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - tackline

9

Hướng dẫn Sun đã được trích dẫn sai ở đây trong chủ đề này.

Lưu ý: Nếu JVM thoát ra trong khi mã thử hoặc bắt đang được thực thi, thì khối cuối cùng sẽ không thực thi. Tương tự như vậy, nếu chuỗi thực thi mã try hoặc catch bị gián đoạn hoặc bị giết, khối cuối cùng sẽ không thực thi mặc dù toàn bộ ứng dụng vẫn tiếp tục.

Nếu bạn xem xét kỹ hướng dẫn sun cho khối cuối cùng, nó không nói "sẽ không thực thi" nhưng "có thể không thực thi" Đây là mô tả chính xác

Lưu ý: Nếu JVM thoát ra trong khi mã thử hoặc bắt đang được thực thi, thì khối cuối cùng có thể không thực thi. Tương tự như vậy, nếu chuỗi thực thi mã try hoặc catch bị gián đoạn hoặc bị giết, khối cuối cùng có thể không thực thi mặc dù toàn bộ ứng dụng vẫn tiếp tục.

Lý do rõ ràng cho hành vi này là, cuộc gọi đến system.exit () được xử lý trong một luồng hệ thống thời gian chạy, có thể mất thời gian để tắt jvm, trong khi đó, bộ lập lịch luồng có thể yêu cầu thực thi cuối cùng. Vì vậy, cuối cùng được thiết kế để luôn thực thi, nhưng nếu bạn đang tắt jvm, có thể xảy ra trường hợp jvm tắt trước khi cuối cùng được thực thi.


6

Ngoài ra nếu một bế tắc / sống động xảy ra bên trong trykhối.

Đây là mã chứng minh điều đó:

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

Mã này tạo ra kết quả sau:

t1 inside lock1
t2 inside lock1

và "cuối cùng" không bao giờ được in


3
Về mặt kỹ thuật, khối try không bao giờ thoát nên khối cuối cùng sẽ không bao giờ có cơ hội thực thi. Điều tương tự cũng có thể nói đối với một vòng lặp vô hạn.
Jeff Mercado

6

Nếu JVM thoát ra trong khi mã thử hoặc bắt đang được thực thi, thì khối cuối cùng có thể không thực thi. ( nguồn )

Tắt bình thường - điều này xảy ra khi luồng không phải daemon cuối cùng thoát HOẶC khi Runtime.exit () ( nguồn )

Khi một luồng thoát ra, JVM thực hiện kiểm kê các luồng đang chạy và nếu các luồng duy nhất còn lại là các luồng daemon, thì nó sẽ bắt đầu một quá trình tắt có trật tự. Khi JVM tạm dừng, mọi luồng daemon còn lại sẽ bị bỏ rơi, cuối cùng các khối không được thực thi, các ngăn xếp không được gỡ bỏ mà JVM chỉ thoát ra. Các luồng daemon nên được sử dụng một cách tiết kiệm, một số hoạt động xử lý có thể bị bỏ rơi một cách an toàn bất cứ lúc nào mà không cần dọn dẹp. Đặc biệt, rất nguy hiểm khi sử dụng các luồng daemon cho các tác vụ có thể thực hiện bất kỳ loại I / O nào. Các luồng Daemon được lưu tốt nhất cho các tác vụ "quản lý nội trợ", chẳng hạn như một luồng nền xóa định kỳ các mục đã hết hạn khỏi bộ nhớ đệm trong bộ nhớ. ( nguồn )

Ví dụ thoát chuỗi không phải daemon cuối cùng:

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

Đầu ra:

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

Trong các trường hợp sau, khối cuối cùng sẽ không được thực thi: -

  • Khi nào System.exit(0)được gọi từ trykhối.
  • Khi JVM hết bộ nhớ
  • Khi quy trình java của bạn bị giết mạnh từ task mgr hoặc bảng điều khiển
  • Tình trạng bế tắc trong trykhối của bạn
  • Khi máy của bạn tắt do mất điện

Cũng có thể có các trường hợp rìa khác, nơi cuối cùng khối sẽ không được thực thi.


1

Có hai cách để dừng thực thi mã khối cuối cùng:
1. Sử dụng System.exit ();
2. Nếu bằng cách nào đó điều khiển thực thi không đạt để thử khối.
Xem:

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

Tôi đã gặp một trường hợp rất cụ thể về khối cuối cùng không thực thi liên quan cụ thể đến khung chơi.

Tôi rất ngạc nhiên khi thấy rằng khối cuối cùng trong mã hành động bộ điều khiển này chỉ được gọi sau một Ngoại lệ, nhưng không bao giờ khi lệnh gọi thực sự thành công.

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

Có lẽ luồng bị kết thúc hoặc một cái gì đó khi renderBinary () được gọi. Tôi nghi ngờ điều tương tự xảy ra với các lệnh gọi kết xuất () khác, nhưng tôi đã không xác minh điều đó.

Tôi đã giải quyết vấn đề bằng cách di chuyển renderBinary () đến sau lần thử / bắt. Điều tra sâu hơn cho thấy play cung cấp chú thích @Finally để tạo một phương thức được thực thi sau khi hành động của bộ điều khiển thực thi. Lưu ý ở đây là điều này sẽ được gọi sau khi thực hiện BẤT KỲ hành động nào trong bộ điều khiển, vì vậy nó có thể không phải lúc nào cũng là một lựa chọn tốt.


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

Cải thiện thụt lề và thêm một số chi tiết.
ROMANIA_engineer

1
Việc thực thi thậm chí sẽ không đến được khối thử bên trong, tất nhiên phần bên trong cuối cùng sẽ không được thực hiện.
Anton Arhipov,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.