java创建线程的两种方式,以及两中方法的联系与优劣,Thread类的常用方法

目录

了解程序,进程,线程的概念

第一种:继承Thread类:

Thread类的常用方法:

第二种:实现Runnable接口:

Threa与Runnable之间的关系:

比较创建线程的两种方式:


了解程序,进程,线程的概念

在理解创建线程之前,我们应该了解下面程序,进程和线程的基本概念:

  1. 程序:是为完成特定任务,用某种语言编写的一组指令的集合,既**一段静态代码**,可以理解成是存储在硬盘中的代码,没有加载到内存中。
  2. 进程:程序一次执行的过程,或是一个正在运行的程序,是动态的。
  3. 线程:程序内部的一条执行路径。

java中创建线程有两种方式:继承Thread类**和**实现Runnable接口

第一种:继承Thread类:

 1. 定义一个继承自Thread的类
 2. 重写run方法
 3. 创建类对象
 4. 调用对象的start方法

//打印100以内能被2整除的整数
class MyThread01 extends Thread{//1.定义一个继承自Thread的类
    public void run() {//2. 重写Thread中的run方法
        for(int i=0 ; i<=100 ; i++) {
            if(i%2==0) {
                System.out.println(getName()+":"+i);
                //getName()代表当前对象的名字
            }
        }
    }
}

public class MyThreadTest_01 {
    public static void main(String[] args) {
        MyThread01 mt = new MyThread01();// 3. 创建类对象
        mt.start();//4. 调用对象的start方法
        
        //打印100以内能被2整除的整数
        for(int i=0 ; i<=100 ; i++) {
            if(i%2==0) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}


start()方法有两个作用:一:启动了当前线程, 二:调用该线程的run方法.

问题一:能否通过直接调用run方法来启动线程?
                答:不能,直接调用run()方法不会启动新的线程,程序执行的顺序和普通的程序一样,先执行run()方法,在执行main方法中的for循环。
                
问题二:打印0到100内的偶数,能否通过已经调用过start()的线程再此执行start()方法?
        答:下面为jdk1.8中Thread的部分源码:
      

通过源码可知:当一个线程的start()已经被调用后,线程中的threeadStatus将不再等于0,如果再此执行start()方法,那么该方法会抛出IllegalThreadStateException()异常。要想解决这个问题,可以再创建一个类对象,如下:

public class MyThreadTest_01 {
    public static void main(String[] args) {
        MyThread01 mt = new MyThread01();
        mt.start();
        
        //mt.start(),mt不能再次调用start()方法,会抛出异常;
        //解决办法:再创建一个MtThread对象
        MyThread01 mt1 = new MyThread01();
        mt1.start();
        
        for(int i=0 ; i<=100 ; i++) {
            if(i%2==0) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

Thread类的常用方法:


run()                              通常要重写父类的run方法,将创建的线程要执行的操作声明在此方法中
start()                            启动当前线程,并调用线程中的run方法
currentThread()           是静态方法,返回当前执行代码的线程
setName()                     为当前的线程设置名字
getName()                     获取当前线程的名字
yield()                            表示释放当前cpu的执行权
sleep()                           是静态方法,强制让当前线程进入阻塞状态,注意:Thread中的run没有抛出异常,所以只能用try...catch,而不能使用throws

join()                              在线程a中调用线程b的join(),此时线程a就进入了阻塞状态,知道b完全执行完线程啊a才结束阻塞状态
isAlive()                         判断当前线程是否还活着

 

第二种:实现Runnable接口:

 1. 第一步:创建一个实现了Runnable接口的类 
 2. 第二步:实现Runnable中的run()方法 
 3. 第三步:创建实现类的对象 
 4. 第四步:将此类对象作为参数传递到Thread的构造器中去,创建Thread对象 
 5. 第五步:通过Thread对象调用start()方法,①启动该线程 ②调用该线程的run()方法

class MyThread02 implements Runnable{//第一步:创建一个实现了Runnable接口的类
    public void run() {//第二步:实现Runnable中的run()方法 
        for(int i=0 ; i<=100 ; i++) {//输出100以内被2整除的整数
            if(i%2==0) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

public class MyThreadTest_02 {
    public static void main(String[] args) {
        
        MyThread02 mythread = new MyThread02();//第三步:创建实现类的对象 
        Thread t1 = new Thread(mythread);//第四步:将此类对象作为参数传递到Thread的构造器中去,创建Thread对象 
        t1.start();//第五步:通过Thread对象调用start()方法
    }
}


不知你们对于该程序最后一行的t1.start()是否有这样的疑惑:t1.start()启动了当前的线程,并调用该线程中的run方法。调用的run方法应该是Thread类中的run方法,但是为何又能调用到MyThread02这个类中的run方法呢?
要想知道答案,还是得看源码:


原因是:Thread类有一种构造器要接受一个Runnable接口对象,如果这个对象不为空,那么Thread的run()方法就会调用这个接口对象中的run()方法!

 Threa与Runnable之间的关系:



通过源码我们可以知道:Thread类实现了Runnable接口。


用图更容易地表示出创建线程的两种方法:

这也就是为什么我们不管用那种方法创建线程,都需要重写run()方法。

比较创建线程的两种方式:


实现Runnable接口的方式相对更好一点
因为java不支持类的多继承,因此没有类的单继承的局限性
实现的方式更适合处理多个线程共享数据的情况(售票问题得以体
现)
 

©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页