首先要明白start
和run
这两个方法。start
方法意为启动一个线程,并不是立马执行这个线程,而是表示这个线程CPU可以去处理了。当CPU去处理这个线程的时候,运行是run
方法中的代码。明白以后可以看下面的几个构造线程的方式。
第一种方式:通过Thread无参构造,构造一个什么也不干的线程
public static void main(String[] args){
// 创建一个线程对象
Thread t = new Thread();
// 调用start方法启动线程,让线程加入CPU处理队列中,并不是立马执行
t.start();
}
这样子就创建并且启动了一个线程,但这个线程什么也没干。我们可以查看Thread的源代码,看看里头的run方法实现。如下:
/* What will be run. */
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
默认情况下,target为null,当我们没有覆盖Thread的run方法并且也没有给target赋值时,就是一个什么也不干的线程。
第二种方式:通过Thread(Runnable)构造一个“能做事”的线程
public static void main(String[] args){
Thread t = new Thread(new Runnable(){
public void run(){
System.out.println("Thread t");
}
});
t.start();
System.out.println("Thread main");
}
这样子也创建了一个线程并且启动了。我们创建了一个Runnable接口匿名实现类,通过Thread(Runnable)
构造,给Thread对象的target属性赋值。main方法执行后,结果有两种情况。
Case 1:
Thread main
Thread t
Case 2:
Thread t
Thread main
大部分情况肯定是第一种。为什么?后面再说。
第三种方式:通过继承Thread类,统一对业务进行处理
1、编写一个类,继承Thread类。这里假设为上面拍卖场景中的“创建出价记录”线程
public class CreateRecordThread extends Thread{
// 覆盖run方法
@Override
public void run(){
// 业务代码,省略
}
}
2、在主线程中创建CreateRecordThread对象并启动
public static void main(String[] args){
Thread t = new CreateRecordThread();
t.start();
}
为什么要这么写?通过第二种方式不是也可以做到吗?原因是如果我们有一个业务需要总是被开启多线程去处理,那么采用第二种方式,这套业务代码可能会出现在多个地方,出现重复代码,维护起来就不方便了。这种方式则可以直接使用对应的线程类来执行业务代码
。当然如果只是某一个地方需要用到,完全可以采用第二种方式去做。
第四种方式:新增业务处理类实现Runnable接口,将业务和线程分离
1、编写一个类,实现Runnable接口
public class CreateRecordRunnable implements Runnable{
public void run(){
// 业务代码,省略
}
}
2、创建线程,运行业务代码
public static void main(String[] args){
Runnable crt = new CreateRecordRunnable();
Thread t = new Thread(crt);
t.start();
}
这么写和第三种写法有什么区别?相对第三种有什么优势?
个人建议(仅供参考)
区别就不说了,一个是类一个是接口,扩展方式不一样。但都可以方便的进行扩展。创建出来的对象不一样,一个是线程类
,一个是业务类
。
“优势”,其实不能说Runnable
相对Thread
有什么“优势”,但我们从代码的合理性去思考。假设我们有一大堆的业务需要开启一个或者多个线程去做,我们可以继承Thread,把业务方法写在run里头。如果代码量很大,我们可以编写多个private方法在run方法中调用
,也可以新建一个父类继承Thread,提供通用方法给子类调用
。我们可以知道,我们的业务处理类,居然是一个线程类。比如说发送邮件的类,比如说发送短信的类,这些类成为了一个线程对象,这并不合理,我们的业务处理对象为什么是一个线程类?我们更希望这些业务处理类是“一个可以被多线程运行的类”
,而非本身就是一个线程。所以,本人总结也仅限本人总结:如果你要用继承Thread的方式,那么还是改为用实现Runnable的方式吧,这样对象的划分会合情合理一些。
(完)