Java常见疑难问题-应用17


55. thread. isInterrupted()与Thread.interrupted()
public class SelfInerruption {
public static void main(String[] args) {
Thread.currentThread().interrupt();
if (Thread.interrupted()) {
// Interruped:false
System.out.println(“Interruped:” + Thread.interrupted());
} else {
System.out.println(“Not interruped:” + Thread.interrupted());
}
}
}
上面结果走的是第一个分支,但结果却不是Interruped:true?

Thread.interrupted()为Thread的静态方法,调用它首先会返回当前线程的中断状态(如果当
前线程上调用了interrupt()方法,则返回true,否则为false),然后再清除当前线程的中断状
态,即将中断状态设置为false。换句话说,如果连续两次调用该方法,则第二次调用将返
回 false。

而isInterrupted()方法为实例方法,测试线程是否已经中断,并不会清除当前线程中断状态。

所以这里应该使用isInterrupted()实例方法,就可以修复该
56. 惰性初始化
public class Lazy {
private static boolean initial = false;
static {
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println(“befor…”);//此句会输出
/*
* 由于使用Lazy.initial静态成员,又因为Lazy还未 初
* 始化完成,所以该线程会在这里等待主线程初始化完成
*/
initial = true;
System.out.println(“after…”);//此句不会输出
}
});
t.start();
try {
t.join();// 主线程等待t线程结束
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
System.out.println(initial);
}
}
看看上面变态的程序,一个静态变量的初始化由静态块里的线程来初始化,最后的结果怎
样?

当一个线程访问一个类的某个成员的时候,它会去检查这个类是否已经被初始化,在这一过
程中会有以下四种情况:
1、 这个类尚未被初始化
2、 这个类正在被当前线程初始化:这是对初始化的递归请求,会直接忽略掉(另,请参考
《构造器中静态常量的引用》一节)
3、 这个类正在被其他线程而不是当前线程初始化:需等待其他线程初始化完成再使用类的
Class对象,而不会两个线程都会去初始化一遍(如果这样,那不类会初始化两遍,这
显示不合理)
4、 这个类已经被初始化
当主线程调用Lazy.main,它会检查Lazy类是否已经被初始化。此时它并没有被初始化(情
况1),所以主线程会记录下当前正在进行的初始化,并开始对这个类进行初始化。这个过
程是:主线程会将initial的值设为false,然后在静态块中创建并启动一个初始化initial的线
程t,该线程的run方法会将initial设为true,然后主线程会等待t线程执行完毕,此时,问
题就来了。
由于t线程将Lazy.initial设为true之前,它也会去检查Lazy类是否已经被初始化。这时,
这个类正在被另外一个线程(mian线程)进行初始化(情况3)。在这种情况下,当前线程,
也就是t线程,会等待Class对象直到初始化完成,可惜的是,那个正在进行初始化工作的
main线程,也正在等待t线程的运行结束。因为这两个线程现在正相互等待,形成了死锁。

修正这个程序的方法就是让主线程在等待线程前就完成初始化操作:
public class Lazy {
private static boolean initial = false;
static Thread t = new Thread(new Runnable() {
public void run() {
initial = true;
}
});
static {
t.start();
}

public static void main(String[] args) {
// 让Lazy类初始化完成后再调用join方法
try {
t.join();// 主线程等待t线程结束
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(initial);
}
}
虽然修正了该程序挂起,但如果还有另一线程要访问Lazy的initial时,则还是很有可
能不等initial最后赋值就被使用了。

总之,在类的初始化期间等待某个线程很可能会造成死锁,要让类初始化的动作序列尽可能
地简单。
57. 继承内部类
一般地,要想实例化一个内部类,如类Inner1,需要提供一个外围类的实例给构造器。一般
情况下,它是隐式地传递给内部类的构造器,但是它也是可以以 expression.super(args) 的方
式即通过调用超类的构造器显式的传递。

public class Outer {
class Inner1 extends Outer{
Inner1(){
super();
}
}
class Inner2 extends Inner1{
Inner2(){
Outer.this.super();
}
Inner2(Outer outer){
outer.super();
}
}
}

class WithInner {
class Inner {}
}
class InheritInner extends WithInner.Inner {
// ! InheritInner() {} // 不能
/*
* 这里的super指InheritInner类的父类WithInner.Inner的默认构造函数,而不是
* WithInner的父类构造函数,这种特殊的语法只在继承一个非静态内部类时才用到,
* 表示继承非静态内部类时,外围对象一定要存在,并且只能在 第一行调用,而且一
* 定要调用一下。为什么不能直接使用 super()或不直接写出呢?最主要原因就是每个
* 非静态的内部类都会与一个外围类实例对应,这个外围类实例是运行时传到内
* 部类里去的,所以在内部类里可以直接使用那个对象(比如Outer.this),但这里
* 是在外部内外 ,使用时还是需要存在外围类实例对象,所以这里就显示的通过构造
* 器传递进来,并且在外围对象上显示的调用一下内部类的构造器,这样就确保了在
* 继承至一个类部类的情况下 ,外围对象一类会存在的约束。
*/
InheritInner(WithInner wi) {
wi.super();
}

public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}

声明: 除非转自他站(如有侵权,请联系处理)外,本文采用 BY-NC-SA 协议进行授权 | 智乐兔
转载请注明:转自《Java常见疑难问题-应用17
本文地址:https://www.zhiletu.com/archives-250.html
关注公众号:智乐兔

赞赏

wechat pay微信赞赏alipay pay支付宝赞赏

上一篇
下一篇

相关文章

在线留言

你必须 登录后 才能留言!

在线客服
在线客服 X

售前: 点击这里给我发消息
售后: 点击这里给我发消息

智乐兔官微