try-catch-finally的执行顺序

try-catch-finally的执行顺序

简化的例子

try-catch-finally中finally块中的代码会在try块中的return语句前执行,现在考虑在try和finally中对同一变量进行值修改的情况,代码如下:

public class TempMain {
    public static int id =0;
    public static void main(String[] args){
        System.out.println("返回:"+changeid());
            System.out.println("最终id:"+id);
    }

       public static int changeid() {
           try {
            id = 1;
            return id;
           } catch (Exception e) {
            return id;
           } finally {
            id =2;
           }
     }
}

执行结果为

返回:1
最终id:2

可见,虽然在函数返回结果前,finally块中的代码将id的值修改为了2,但函数的返回结果未受影响,仍然返回了1。
用javap查看这段代码的字节码,得到:

这里写图片描述

其中 Locals = 3 表示本地变量的slot个数(编号0-2)

行号指令含义
0iconst1将int类型常量值1推到栈顶
1putstatic将栈顶的赋值给静态常量id
4getstatic访问(取得)静态变量id的值
7istore_2将当前值存入本地变量的
8iconst_2将int类型常量值2推到栈顶
9putstatic将栈顶的赋值给静态常量id
12iload_2第3个slot中的本地变量加载到栈顶
13ireturn返回栈顶值

可以看到,虽然有将静态变量id赋值为2的操作,但真正返回的是在执行finally块之前就存在第三个slot中的“1”。
将代码简单更改,在finally中也添加return语句

public class TempMain {
    public static int id =0;
    public static void main(String[] args){
        System.out.println("返回:"+changeid());
            System.out.println("最终id:"+id);
    }

        public static int changeid() {
        try {
            id = 1;
            return id;
        } catch (Exception e) {
            return id;
        } finally {
            id =2;
            return id;
        }
    }
}

执行结果为

返回:2
最终id:2

来看看这次生成的字节码

这里写图片描述

行号指令含义
0iconst1将int类型常量值1推到栈顶
1putstatic将栈顶的赋值给静态常量id
4goto 12执行行号12的操作
12iconst_2将int类型常量值2推到栈顶
13putstatic将栈顶的赋值给静态常量id
16getstatic访问(取得)静态变量id的值
19ireturn返回栈顶值

这一次,由于finally中含有return语句,因此直接将静态变量id的最新值返回给了调用方。

并发的例子

前面简化的例子,证明了return语句最好写在try中,除非你确实想在finally中对返回结果再进行修改。来看看try-catch-finally对并发结果的影响。

public class TempMain {
    public static int id =0;
        static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args){
        Thread thread1 = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("T1 return "+changeid(1));
            }});

        Thread thread2 = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("T2 return "+changeid(2));
            }});

        thread1.start();
        thread2.start();

    }

    @SuppressWarnings("finally")
    public static int changeid(int nid){
        lock.lock();
        try{
            id = nid ;
        }catch(Exception e){
            return id;
        }finally{
            lock.unlock();
            if(nid==1){
                try {
                    Thread.currentThread().sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return id;
        }
    }
 }

这段代码中,两个线程调用了同一个函数,该函数会修改静态变量id的值,并且如果是线程1调用的话,会sleep 5秒。按照期望,线程1将得到返回值1,线程2将得到返回值2,但运行的结果:

T2 return 2
T1 return 2

原因是,将return语句写在了finally中,此时,先执行的线程1已经放弃了锁,但由于需要sleep 5秒,因此并没有返回结果。但这时线程2得到了锁并修改了id的值,之后,线程1 sleep结束,返回结果。还记得前面例子中第二段代码么?返回的不是slot中的值,而是反回了getstatic的值,也就是id的值。因此,线程1的返回值是id的最新值—2。

为了避免这种由于过早释放锁导致返回结果被修改的情况,办法就是保持将return写在try中的习惯

public class TempMain {
    public static int id =0;
        static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args){
        Thread thread1 = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("T1 return "+changeid(1));
            }});

        Thread thread2 = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("T2 return "+changeid(2));
            }});

        thread1.start();
        thread2.start();

    }

    @SuppressWarnings("finally")
    public static int changeid(int nid){
        lock.lock();
        try{
            id = nid ;
            return id;
        }catch(Exception e){
            return id;
        }finally{
            lock.unlock();
            if(nid==1){
                try {
                    Thread.currentThread().sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 }

这一次得到正确的结果,因为线程1返回的是存在slot(本地变量)中的值。

T2 return 2
T1 return 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值