【Java】使用外部变量初始化子线程 上

问题

如何使用外部变量初始化子线程

来源

写C#的时候发现的问题,现在用Java来说明一下(为什么用Java?)。通常在创建子线程的时候需要向子线程引入一些外部变量来进行初始化,这时候可以使用2种方式来通过编译(仅仅通过编译而已),那么哪种方法才是正确的呢?

代码

import java.util.Calendar;
import java.util.Random;

public class Main {

    int tInt;

    String tString;

    final int Test_Cnt = 10;

    public static void main(String[] args) {

        // new Main().threadTestA();

        // new Main().threadTestB();

        // new Main().threadTestC();

        // new Main().threadTestD();

        // new Main().threadTestE();
    }

    public void threadTestA() {

        Thread[] threads = new Thread[Test_Cnt];

        for(int i = 0; i < Test_Cnt; ++i) {

            tString = String.format("thread %d", i); // (1)

            threads[i] = new Thread() {

                @Override
                public void run()
                {
                    Random r = new Random(Calendar.getInstance().getTimeInMillis());

                    try { Thread.sleep(1000 + r.nextInt(2000)); } catch (InterruptedException e) {  }

                    System.out.println(tString);
                }
            };
        }

        for(Thread thr : threads) thr.start();

        for(Thread thr : threads) try { thr.join(); } catch (InterruptedException e) { }
    }

    public void threadTestB() {

        Thread[] threads = new Thread[Test_Cnt];

        for(int i = 0; i < Test_Cnt; ++i) {

            tInt = i; // (2)

            threads[i] = new Thread() {

                @Override
                public void run()
                {
                    Random r = new Random(Calendar.getInstance().getTimeInMillis());

                    try { Thread.sleep(1000 + r.nextInt(2000)); } catch (InterruptedException e) {  }

                    System.out.println(tInt);
                }
            };
        }

        for(Thread thr : threads) thr.start();

        for(Thread thr : threads) try { thr.join(); } catch (InterruptedException e) { }
    }

    public void threadTestC() {

        Thread[] threads = new Thread[Test_Cnt];

        for(int i = 0; i < Test_Cnt; ++i) {

            final String ftString = String.format("thread %d", i); // (3)

            threads[i] = new Thread() {

                @Override
                public void run()
                {
                    Random r = new Random(Calendar.getInstance().getTimeInMillis());

                    try { Thread.sleep(1000 + r.nextInt(2000)); } catch (InterruptedException e) {  }

                    System.out.println(ftString);
                }
            };
        }

        for(Thread thr : threads) thr.start();

        for(Thread thr : threads) try { thr.join(); } catch (InterruptedException e) { }
    }

    public void threadTestD() {

        Thread[] threads = new Thread[Test_Cnt];

        for(int i = 0; i < Test_Cnt; ++i) {

            final int itInt = i; // (4)

            threads[i] = new Thread() {

                @Override
                public void run()
                {
                    Random r = new Random(Calendar.getInstance().getTimeInMillis());

                    try { Thread.sleep(1000 + r.nextInt(2000)); } catch (InterruptedException e) {  }

                    System.out.println(itInt);
                }
            };
        }

        for(Thread thr : threads) thr.start();

        for(Thread thr : threads) try { thr.join(); } catch (InterruptedException e) { }
    }

    public void threadTestE() {

        Thread[] threads = new Thread[Test_Cnt];

        for(int i = 0; i < Test_Cnt; ++i) {

            tInt = i; // (5)

            threads[i] = new Thread() {

                @Override
                public void run()
                {
                    int rInt = tInt; // (5)

                    Random r = new Random(Calendar.getInstance().getTimeInMillis());

                    try { Thread.sleep(1000 + r.nextInt(2000)); } catch (InterruptedException e) {  }

                    System.out.println(rInt);
                }
            };
        }

        for(Thread thr : threads) thr.start();

        for(Thread thr : threads) try { thr.join(); } catch (InterruptedException e) { }
    }
}

说明

主要区别使用 // (num) 形式表明了。选用 Stringint 是为了验证值类型和引用类型的区别。

结果表明 threadTestAthreadTestB 都不能正确执行,更具体的说它们输出的数字都是9。因为这10个线程并没有在定义时获得变量 i 的值,它们被要求在需要该值时向外部的成员变量 sIntsString 取值,而在生成线程的循环中这2个变量就已经被修改的面目全非了(poi~了相了),当这10个线程开始执行的时候,自然获取到的是sIntsString 在最后的修改中保留下的值。

threadTestCthreadTestD 可以如期运行。因为 final 的约束,局部变量被赋值后即不可修改,从而保证了各子线程在取值时能够取到其上下文中定义的值(按理说局部变量应该分配在栈中,但是我感觉这种被 final 修饰的局部变量应该被分配到了堆里,当然我目前没能力证明囧~) ,看上去和闭包有点像了,毕竟内部类依赖外部类中的局部变量,从而延长了外部类局部变量的生命周期。

threadThreadE 也不能正确执行,脑抽的时候写的,没什么参考价值。

总结

所以说需要使用外部变量初始化子线程的时候需要使用 final 进行修饰,当然语法已经这么限制了(对于局部变量),而且这个语法针对的(不是某个类 我是说Java的各种类都是辣鸡 大雾)是内部类,而不是单独为线程服务。使用外部成员变量的时候务必小心上文中的那种方式,很可能会产生N个一样的子线程。当然如果只生成1个子线程,那么这2种方法都没有问题,需要考虑的只有主线程和子线程对成员变量的竞争而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值