概述
静态初始化块里启动新线程的陷阱
一:问题的提出
我们知道静态块帮助我们完成一些类的初始化的工作,那么在静态块里面启动一个线程,让它来帮助我们来完成初始化的工作会发生什么哪?死锁还是输出了让我们感觉不是我们想要的结果呐?,不妨试试看,结果是什么吧.
二:具备的知识
多线程的基础知识, 多线程的创建,启动,join()方法和匿名内部类的使用等.例子比较简单的.
三:写代码前的分析
我们知道,main线程开始执行对类的初始化的时候,初始化的主要的步骤就是两个
(1):为这个类的所有的静态的变量分配内存空间.
(2):调用静态初始化块代码执行初始化.
注意这两者之间的顺序哟.
当某一个线程访问一个静态变量的时候,这个类的状态会分大概四种情况的:
(1):该类尚未被初始化,当前线程开始对其进行初始化.
(2):该类正在执行初始化,当前线程就会 递归的执行初始化.
(3):这个类正在被其他的线程执行初始化,当前的线程就会暂停,等待其他的线程初始化完成.
(4):这个类已经初始化完成啦,就会直接得到这个静态变量的值
四:案例代码
public class StaticThread {
static{
//线程类使用一次推荐使用匿名内部类来实现
Thread thread=new Thread(){
//启动新线程将website属性进行赋值
public void run(){
System.out.println("进入run()方法");
System.out.println(website+"2");
website="www.baidu.com1";
System.out.println("退出run()方法");
}
};
thread.start();
//让当前的main线程等待新线程执行完毕
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//定义一个静态的变量,设置初始值
static String website="www.baidu.com";
public static void main(String[] args) {
System.out.println(StaticThread.website+"1");
}
}
运行结果:
看看结果发生了死锁.(两个线程互相等待对方执行,因此都不会像下执行).
分析死锁发生的原因:
首先是mian线程开始对这个类进行初始化,对静态的变量分配内存空间,这个时候的值为null,然后就会执行那个静态初始化块.在静态初始化块里面创建一个线程,并且启动了这个新线程,并且调用了join的方法.这就意味着mian线程必须等待新线程执行完毕后才可以向下执行的.(静态变量属于类),但是这个类正在由main线程初始化,因此新的线程就会暂停等待main线程对这个类执行初始化结束的.不出意外的出现了死锁.
新的线程开始执行之后,首先会按照线程体里面的代码顺序执行.看到输出的那句话后,接下来就是开始访问那个静态变量。
接下来我们去掉那个join()方法的调用,不让mian线程等待新线程嘛.
public class StaticThread {
static{
//线程类使用一次推荐使用匿名内部类
Thread thread=new Thread(){
//启动新线程将website属性进行赋值
@Override
public void run(){
System.out.println("进入run()方法");
System.out.println(website);
website="www.huya.com";
System.out.println("退出run()方法");
}
};
thread.start();
}
//定义一个静态的变量,设置初始值
static String website="www.baidu.com";
public static void main(String[] args) {
System.out.println(StaticThread.website);
}
}
运行结果:
发现那个静态块里面的修改丝毫没有起作用
很显然,那个新线程是在类初始化后完成的.调用了start()只是进入了就绪状态,虽然线程修改了那个静态变量的值,但是陈序不在访问它了.
可以在添加一个Thread.sleep(10)给新线程一个表现自己的机会嘛,毕竟主线程执行类的初始化也挺累的.但是就算给新线程机会了,输出的结果还是一样的,没有变化,只因为主线程对静态变量的初始化还没有完成的.而新线程又要访问那个变量的.这个在前面分析过了,当前的新线程要暂停等待main线程来完成剩下的初始化工作的.直到完成为止.main线程对那个类的初始化工作完成后,那个静态变量的值才是那个值,执行main()方法的第一行代码就输出了那个值.这是才切换到了新的线程继续执行线程体里面的代码.
发现一点:静态初始化块里面启动多线程对静态变量所赋的值不是初始化的,而是一次普通的赋值操作.
测试代码如下:
public class StaticThread {
static{
//线程类使用一次推荐使用匿名内部类
Thread thread=new Thread(){
//启动新线程将website属性进行赋值
@Override
public void run(){
System.out.println("进入run()方法");
System.out.println(website);
website="www.huya.com";
System.out.println("退出run()方法");
}
};
thread.start();
}
//定义一个静态的变量,设置初始值
static String website;
public static void main(String[] args) {
System.out.println(StaticThread.website);
}
}
运行结果:
发现根本不是初始化,而是一般的赋值,如果不是就会运行输出值的.
为了更加清楚的说明可以加一个final修饰符,发现编译不通过.无法为最终的变量赋值。
五:简单总结
静态块里面的代码不一定都是类的初始化的,静态块的启动新的线程的就是新的线程方法执行体的,并不是类的初始化!相同的道理:放在非静态块里面的新线程的run()方法也就是线程的执行体而已,不参与对象的初始化工作的. 算是多线程结合静态块的使用的一点陷阱吧,注意一点就避免一个bug吧.
}
最后
以上就是踏实戒指为你收集整理的静态初始化块里启动新线程的陷阱的全部内容,希望文章能够帮你解决静态初始化块里启动新线程的陷阱所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复