目录
线程创建的说明
线程的创建分两步: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()); } }
运行结果:
解读:
- thread1和thread2的线程ID不同,thread2和主线程ID相同,说明通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;
- 虽然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