1.怎么开启线程,主要有两种方法继承Thread类与实现Runnable或者Callable接口,但其实Runnable与Callable很类似,只不过Callable里面定义了回调的返回值且可声明抛出异常而已。
三种创建线程方式对比:
在现实中一般推荐采用实现Runnable和Callable接口的方式来创建多线程,因为Java不支持子类多继承,所以当线程已经继承了Thread类就不能再继承其他父类了。
2.再看两个创建线程的例子:
public class ThreadTest {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
while (true){
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1: " + Thread.currentThread().getName());
System.out.println("2: " + this.currentThread().getName());
}
}
};
thread.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1: " + Thread.currentThread().getName());
}
}
});
thread2.start();
}
}
Thread thread = new Thread();本身就已经创建了线程,继承父类run方法:
public void run() {
if (target != null) {
target.run();
}
}
可见Thread下的run方法仅仅是对线程做了个非空的判断,但这不是我们想要的方法,所以要覆盖父类的run方法,即如上代码所示在覆盖的run方法中写自己的代码。(如果sleep(毫秒)了就要try-catch)
再回到上面两种方法,第一个是new Thread 覆盖run方法,第二种是new的Runnable去写run方法。
问:Runnable不是接口吗,怎么能实例化new Runnable()了?
答:因为匿名内部类,在《Java核心技术卷1》253页的匿名内部类节中写道:“如果构造参数的闭小括号后面跟一个开大括号,正在定义的就是匿名内部类”。对于上面的new Runnable可以理解成:
class Anonymous implement Runnable(){
@ Override
public void run(){
}
}
Runnable runnable = new Anonymous();
故实质并不是实例化接口。抽象类也是一样比如某些类abstrct Animal 本身就不应该设计成可以实例化因为“动物”是抽象的,只有具体的猫、狗才能实例化,否则没意义。
所以以下也是可以的,不过要覆盖所有接口实现的方法
问:为什么在第一种方法打印的时候可以用this第二种不可以。
答:this表示的是当前方法所指的对象,即run()所指的对象Thread,但第二种方this指的就是new Runnable(),指不到Thread故不能用。