Java并发编程之线程知识二:线程的创建

目录

线程创建的说明

创建方式一:继承java.lang.Thread类

创建方式二:实现java.lang.Runnable接口

两种创建线程方式的对比


线程创建的说明

线程的创建分两步:1.实现接口java.lang.Runnable重写其run方法;2.创建线程对象,通过start()方法去启动线程。

线程的创建有两种方式:1.继承java.lang.Thread类;2.实现java.lang.Runnable接口

其实java.lang.Thread也是实现了java.lang.Runnable接口。

自定义类通过实现Runnable接口并重写run方法与java.lang.Thread类实现Runnable接口是一样的,两者都需要在run方法中实现自己的业务逻辑,区别在于:java.lang.Thread类是Java自带的,run方法内部没有实现逻辑,需要继承它才能写出自己的业务逻辑

创建方式一:继承java.lang.Thread类

继承Thread类的话,必须重写run方法,在run方法中定义需要执行的任务。

通过继承继承Thread类创建自己的线程 :

class MyThread extends Thread{
    private static int num = 0;
    public MyThread(){
        num++;
    }
     
    @Override
    public void run() {
        System.out.println("主动创建的第"+num+"个线程");
    }
}

创建好了自己的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线程。注意,不是调用run()方法启动线程run方法中只是定义需要执行的任务,如果调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。

public class Test {
    public static void main(String[] args)  {
        MyThread thread = new MyThread();
        thread.start();
    }
}

 区分start()方法与run()方法:

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
}
 
 
class MyThread extends Thread{
    private String name;
     
    public MyThread(String name){
        this.name = name;
    }
     
    @Override
    public void run() {
        System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
    }
}

运行结果:

解读:

  1. thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;
  2. 虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。

创建方式二:实现java.lang.Runnable接口

通过实现Runnable接口来实现必须重写其run方法,在run方法中定义需要执行的任务。

两种创建线程的方式都需要在run方法中有自己的业务处理。

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}
 
 
class MyRunnable implements Runnable{
    public MyRunnable() {   
    }
    @Override
    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}

Runnable的中文意思是“任务”,顾名思义,通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行注意:这种方式必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务如果调用Runnable的run方法的话,是不会创建新线程的,这根普通的方法调用没有任何区别。

两种创建线程方式的对比

举例:

吃苹果比赛

想象一个这样的例子:给出一共50个苹果,让三个同学一起来吃,并且给苹果编上号码,让他们吃的时候顺便要说出苹果的编号:

运行结果可以看到,使用继承方式实现,每一个线程都吃了50个苹果。这样的结果显而易见:是因为显式地创建了三个不同的Person对象,而每个对象在堆空间中有独立的区域来保存定义好的50个苹果。

而使用实现方式则满足要求,这是因为三个线程共享了同一个Apple对象,而对象中的num数量是一定的。

所以可以简单总结出继承方式和实现方式的区别:

对于这两种方式哪种好并没有一个确定的答案,它们都能满足要求。就我个人意见,我更倾向于实现Runnable接口这种方法。因为线程池可以有效的管理实现了Runnable接口的线程,如果线程池满了,新的线程就会排队等候执行,直到线程池空闲出来为止。而如果线程是通过实现Thread子类实现的,这将会复杂一些。

有时我们要同时融合实现Runnable接口和Thread子类两种方式。例如,实现了Thread子类的实例可以执行多个实现了Runnable接口的线程。一个典型的应用就是线程池。

线程池核心源码中runWorker方法的处理逻辑如下:

-------------------------------------------------------------------
链接:https://www.jianshu.com/p/cd9d0927be35
链接:https://www.cnblogs.com/dolphin0520/p/3913517.html

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值