今天开学第一天,进行了一次小测验,有道题目是这样的
public class Test extends Thread implements Runnable { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { System.out.println("This is the test"+i); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Thread thread = new Thread(new Test()); thread.start(); } }
问程序是否正确执行,会不会报错,大家都知道创建线程有两种方式:1、继承Thread类,复写其run()方法;(这种方式下,每创建一个子类对象就创建了一个线程)
优点是,代码简单,直接编写子类继承Thread类,然后调用new子类对象,直接调用父类的start()方法,start()方法中会调用子类中复写的run()方法,执行自定义的动作。
缺点是,一旦一个类继承了其他的类,就不可以再继承Thread类了。
2、实现Runnable接口,Runnable接口中有一个run()方法,把要执行的代码放到run()方法中。
优点,解决了Java中单继承的局限性,线程实现了Runnable接口,还可以继承其他对象,该方式创建线程时,会传入一个实现了Runnable接口的对象,可以多个线程共享这个实现了Runnable接口的对象,适合多线程处理同一份资源的情况(典型例子就是,多窗口同时卖票)
缺点,较之继承线程的方式创建线程要稍微复杂一些。
实际开发过程中,提倡使用第二种方式创建线程,这种方式比较灵活,更具扩展性。
下面来看下上面的那个程序,Test类既继承了Thread类,又实现了Runnable接口,主函数中
Thread thread = new Thread(new Test());其实是使用了第二种创建线程的方式,其中传入的对象new Test()是实现了Runnable接口的,这里大家要记住的是Thread类也是实现了Runnable接口的,同样Thread类的子类也是实现了Runnable接口的,所以以上程序改成下面这样也是可以的,
public class Test1 extends Thread { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { System.out.println("This is the test"+this.currentThread().getName()+" "+i); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub // Test1 t1 = new Test1(); // t1.start(); Thread thread = new Thread(new Test1()); thread.start(); } }运行结果截图:
大家可以看到运行结果:当前线程名为:Thread-1,大家知道线程的默认命名是按照Thread-x(x是从0开始的整数)的规则命名的,那么,Thread-0那个线程呢?
这是因为该类继承了Thread类,当创建一个子类对象时,就会创建一个线程,但是线程要运行的话,一定要调用它的start()方法来开启线程。