Um bloco finally sempre é executado?


112

Existe alguma condição em que finalmente não pode ser executado em java? Obrigado.


13
Essa é uma pergunta que você pode receber ao tentar conseguir um emprego em uma determinada empresa conhecida?
Tom Hawtin - tackline

@ TomHawtin-tackline Cuidados para nomeá-lo? (Deus, eu perdi a idade desse post!)
Hele

6
@Hele, eu não gostaria de dar o jogo, mas você pode pesquisar no Google.
Tom Hawtin - tackline

resposta curta: sim, em condições normais.
Shark

Respostas:


139

dos tutoriais da Sun

Nota: Se a JVM sair enquanto o código try ou catch está sendo executado, o bloco finally pode não ser executado. Da mesma forma, se o encadeamento que executa o código try ou catch for interrompido ou eliminado, o bloco finally pode não ser executado mesmo que o aplicativo como um todo continue.

Não conheço nenhuma outra maneira de o bloco finally não ser executado ...


6
@dhiller - Tenho certeza de que "desligar" está incluído em "Se a JVM sair ..." :-p
Jason Coco

2
@Jason Coco: Terminar (como na perda de energia) não é exatamente a mesma coisa que sair; o último é um processo mais ou menos organizado que culmina no primeiro. ; p
user359996

2
AFAIK, se um tópico é interrompido, não é interrompido imediatamente. Cabe ao código no thread detectar a interrupção e parar sua tarefa, então, finalmente, o código deve ser executado.
Bart van Heukelom

2
Eu acho que se uma exceção for lançada no bloco finally, o resto do bloco não será executado.
Adriaan Koster,

bem, isso é uma merda!
eiran de

63

System.exit desliga a máquina virtual.

Encerra a Java Virtual Machine atualmente em execução. O argumento serve como um código de status; por convenção, um código de status diferente de zero indica encerramento anormal.

Este método chama o exitmétodo em classe Runtime. Este método nunca retorna normalmente.

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

"bye" não é impresso no código acima.


Além disso, uma exceção deve desligar o Java Virtual Machine.
kaissun de

3
Se uma exceção ocorrer durante a execução de System.exit (0), então o bloco finalmente será executado.
Halil

50

Apenas para expandir o que os outros disseram, qualquer coisa que não cause algo como a saída da JVM incorrerá no bloqueio finally. Portanto, o seguinte método:

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

irá estranhamente compilar e retornar 1.


2
isso realmente me confundiu por um bom par de horas algumas semanas atrás.
nickf

3
É considerado uma má ideia retornar um valor de um bloco finally. Retorne apenas do bloco try ou retorne de fora do bloco try / finally. A maioria dos IDEs marcará isso com um aviso.
Ran Biron

1
@nickf Percebi que você não está mais confuso. Você poderia explicar a mecânica de por que 1 é retornado e não 0. Eu só poderia adivinhar que a memória (ou é um registro) que armazena o valor de retorno da função que inicialmente contém 0, é sobrescrita quando o bloco finally é executado .
Yaneeve

2
Isso é curioso, em C # não é permitido retornar de um bloco finally.
JMCF125

3
@RanBiron Claro. Ele não estava realmente recomendando retornar dentro de um bloco finally, ele estava apenas tentando demonstrar que mesmo uma instrução return ainda fará com que o código desse bloco seja executado.
Aquarelle

15

Relacionado a System.exit, também existem certos tipos de falha catastrófica em que um bloco final pode não ser executado. Se a JVM ficar totalmente sem memória, ela pode simplesmente sair sem travar ou finalmente acontecer.

Especificamente, lembro-me de um projeto em que tentamos tolamente usar

catch (OutOfMemoryError oome) {
    // do stuff
}

Isso não funcionou porque a JVM não tinha mais memória para executar o bloco catch.


Quando OutOfMemoryError é lançado, geralmente há muita memória restante (para interromper o thrashing do GC). No entanto, se você pegá-lo repetidamente, obviamente retornará ao problema do GC.
Tom Hawtin - tackline

Achei que não se deveria pegar exceções não verificadas!
Sergii Shevchyk

1
Tentei do meu lado, usando jdk7, mas pegou o erro OutOfMemory!
Jaskey

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

Nesse caso, o finally não será executado (a menos que o obsoleto Thread.stopseja chamado, ou um equivalente, digamos, por meio de uma interface de ferramentas).


Esta página afirma que um erro ThreadDeath é lançado e que a pilha é desfeita normalmente quando Thread.stop () é chamado. Existe uma pegadinha que estou perdendo? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

Eu não acho que haja um problema. Talvez você esteja imaginando uma captura que não existe. Se inserirmos um explícito throw, o finallybloco será executado conforme o esperado. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - tackline

9

O tutorial do Sun foi citado erroneamente aqui neste tópico.

Nota: Se a JVM sair enquanto o código try ou catch estiver sendo executado, o bloco finally não será executado. Da mesma forma, se o thread que executa o código try ou catch for interrompido ou eliminado, o bloco finally irá não executado, embora o aplicativo como um todo continue.

Se você olhar atentamente para o tutorial do sol para o bloco final, ele não diz "não será executado", mas "pode ​​não ser executado" Aqui está a descrição correta

Nota: Se a JVM sair enquanto o código try ou catch está sendo executado, o bloco finally pode não ser executado. Da mesma forma, se o encadeamento que executa o código try ou catch for interrompido ou eliminado, o bloco finally pode não ser executado mesmo que o aplicativo como um todo continue.

A razão aparente para esse comportamento é que a chamada para system.exit () é processada em um thread do sistema em tempo de execução que pode demorar para desligar o jvm, enquanto o planejador de thread pode finalmente pedir para ser executado. Então, finalmente, é projetado para executar sempre, mas se você estiver encerrando o jvm, pode acontecer que o jvm seja encerrado antes de finalmente ser executado.


6

Além disso, se um deadlock / livelock acontecer dentro try bloco.

Aqui está o código que demonstra isso:

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

Este código produz a seguinte saída:

t1 inside lock1
t2 inside lock1

e "finalmente" nunca é impresso


3
Tecnicamente, o bloco try nunca sai, então o bloco finally nunca deve ter a chance de ser executado. O mesmo pode ser dito para um loop infinito.
Jeff Mercado

6

Se a JVM sair enquanto o código try ou catch estiver sendo executado, o bloco finally pode não ser executado. ( fonte )

Desligamento normal - isso ocorre quando o último segmento não-daemon sai OU quando Runtime.exit () ( fonte )

Quando um encadeamento é encerrado, a JVM executa um inventário de encadeamentos em execução e, se os únicos encadeamentos restantes forem encadeamentos daemon, ele inicia um encerramento ordenado. Quando a JVM é interrompida, quaisquer encadeamentos de daemon restantes são abandonados, finalmente os blocos não são executados, as pilhas não são desenroladas, a JVM apenas sai. Os encadeamentos daemon devem ser usados ​​com moderação; poucas atividades de processamento podem ser abandonadas com segurança a qualquer momento, sem limpeza. Em particular, é perigoso usar threads de daemon para tarefas que podem realizar qualquer tipo de E / S. Os encadeamentos daemon são melhor salvos para tarefas de "manutenção", como um encadeamento em segundo plano que remove entradas expiradas periodicamente de um cache na memória. ( fonte )

Exemplo de últimas saídas de thread não 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.");
    }
}

Resultado:

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

Nos seguintes casos, o bloco final não será executado: -

  • Quando System.exit(0)é invocado do trybloco.
  • Quando a JVM fica sem memória
  • Quando o seu processo java é eliminado à força do gerenciador de tarefas ou console
  • Condição de deadlock em seu trybloco
  • Quando sua máquina desliga devido a falha de energia

Também pode haver outros casos marginais, onde o bloco final não será executado.


1

Existem duas maneiras de parar finalmente a execução do código:
1. Use System.exit ();
2. Se de alguma forma, o controle de execução não chega a tentar bloquear.
Vejo:

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

Eu me deparei com um caso muito específico do bloco finally não executando, relacionado especificamente à estrutura de reprodução.

Fiquei surpreso ao descobrir que o bloco finally neste código de ação do controlador só foi chamado depois de uma exceção, mas nunca quando a chamada realmente foi bem-sucedida.

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

Talvez o thread seja encerrado ou algo assim quando renderBinary () é chamado. Eu suspeitaria que a mesma coisa acontece com outras chamadas render (), mas não verifiquei isso.

Resolvi o problema movendo renderBinary () para depois de try / catch. Uma investigação mais aprofundada revelou que play fornece uma anotação @Finally para criar um método que é executado após a execução de uma ação do controlador. A ressalva aqui é que ele será chamado após a execução de QUALQUER ação no controlador, portanto, nem sempre é uma boa escolha.


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

Melhore a indentação e adicione alguns detalhes.
ROMANIA_engineer

1
A execução nem chegaria ao bloco try interno, é claro que o interno finalmente não seria executado.
Anton Arhipov
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.