本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!前端
这是我在掘金的第五篇文章了。感谢阅读,并但愿之后持续关注,我会输出更多技术干货,咱们共同进步!java
之后可能会分为几大专题,相似于并发专题,源码专题,面试专题等(只会分享干货)。面试
感兴趣的能够点击我头像查看历史文章,每一篇都是干货哦!编程
建立线程到底有几种方式,你都知道哪几种?后端
咱们都知道实现线程是并发编程中基础中的基础,由于咱们必需要先实现多线程,才能够继续后续的一系列操做。因此咱们今天就先从并发编程的基础如何实现线程开始讲起,虽然实现线程看似简单、基础,但实际上却暗藏玄机。markdown
实现线程的方式到底有几种?大部分人会说有 2 种、3 种或是 4 种,不多有人会说有 1 种。那你们可能随口说出来的就是两种,先说你们熟悉的两种方式!多线程
public class RunnableThread implements Runnable {
@Override
public void run() {
System.out.println('用实现Runnable接口实现线程');
}
}
复制代码
第 1 种方式是经过实现 Runnable 接口实现多线程,如代码所示,首先经过 RunnableThread 类实现 Runnable 接口,而后重写 run() 方法,以后只须要把这个实现了 run() 方法的实例传到 Thread 类中就能够实现多线程。架构
public class ExtendsThread extends Thread {
@Override
public void run() {
System.out.println('用Thread类实现线程');
}
}
复制代码
第 2 种方式是继承 Thread 类,如代码所示,与第 1 种方式不一样的是它没有实现接口,而是继承 Thread 类,并重写了其中的 run() 方法。相信上面这两种方式你必定很是熟悉,而且常常在工做中使用它们。并发
那么为何说还有第 3 种或第 4 种方式呢?咱们先来看看第 3 种方式:经过线程池建立线程。线程池确实实现了多线程,好比咱们给线程池的线程数量设置成 10,那么就会有 10 个子线程来为咱们工做,接下来,咱们深刻解析线程池中的源码,来看看线程池是怎么实现线程的?dom
static class DefaultThreadFactory implements ThreadFactory {
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
复制代码
对于线程池而言,本质上是经过线程工厂建立线程的,默认采用 DefaultThreadFactory ,它会给线程池建立的线程设置一些默认值,好比:线程的名字、是不是守护线程,以及线程的优先级等。可是不管怎么设置这些属性,最终它仍是经过 new Thread() 建立线程的 ,只不过这里的构造函数传入的参数要多一些,由此能够看出经过线程池建立线程并无脱离最开始的那两种基本的建立方式,由于本质上仍是经过 new Thread() 实现的。
class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return new Random().nextInt();
}
}
//建立线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//提交任务,并用 Future提交返回结果
Future<Integer> future = service.submit(new CallableTask());
复制代码
第 4 种线程建立方式是经过有返回值的 Callable 建立线程,Runnable 建立线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们能够把线程执行的结果做为返回值返回,如代码所示,实现了 Callable 接口,而且给它的泛型设置成 Integer,而后它会返回一个随机数。
可是,不管是 Callable 仍是 FutureTask,它们首先和 Runnable 同样,都是一个任务,是须要被执行的,而不是说它们自己就是线程。它们能够放到线程池中执行,如代码所示, submit() 方法把任务放到线程池中,并由线程池建立线程,无论用什么方法,最终都是靠线程来执行的,而子线程的建立方式仍脱离不了最开始讲的两种基本方式,也就是实现 Runnable 接口和继承 Thread 类。 是的 没错,本质仍是Runnable和Thread
关于这个问题,咱们先不聚焦为何说建立线程只有一种方式,先认为有两种建立线程的方式,而其余的建立方式,好比线程池或是定时器,它们仅仅是在 new Thread() 外作了一层封装。
若是咱们把这些都叫做一种新的方式,那么建立线程的方式便会变幻无穷、层出不穷,好比 JDK 更新了,它可能会多出几个类,会把 new Thread() 从新封装,表面上看又会是一种新的实现线程的方式,透过现象看本质,打开封装后,会发现它们最终都是基于 Runnable 接口或继承 Thread 类实现的!
@Override
public void run() {
if (target != null) {
target.run();
}
}
复制代码
首先,启动线程须要调用 start() 方法,而 start() 方法最终还会调用 run() 方法,咱们先来看看第一种方式中 run() 方法到底是怎么实现的,能够看出 run() 方法的代码很是短小精悍,第 1 行代码 if (target != null) ,判断 target 是否等于 null,若是不等于 null,就执行第 2 行代码 target.run(),而 target 实际上就是一个 Runnable,即便用 Runnable 接口实现线程时传给Thread类的对象。
而后,咱们来看第二种方式,也就是继承 Thread 方式,实际上,继承 Thread 类以后,会把上述的 run() 方法重写,重写后 run() 方法里直接就是所须要执行的任务,但它最终仍是须要调用 thread.start() 方法来启动线程,而 start() 方法最终也会调用这个已经被重写的 run() 方法来执行它的任务.
这时咱们就能够完全明白了,事实上建立线程只有一种方式,就是构造一个 Thread 类,这是建立线程的惟一方式。
本质上,实现线程只有一种方式,而要想实现线程执行的内容,却有两种方式,也就是能够经过 实现 Runnable 接口的方式,或是继承 Thread 类重写 run() 方法的方式,把咱们想要执行的代码传入,让线程去执行,在此基础上,若是咱们还想有更多实现线程的方式,好比线程池和 Timer 定时器,只须要在此基础上进行封装便可。
抛出问题,咱们会想虽然只有一种建立线程的方法,那实现 Runnable 接口比继承 Thread 类实现线程哪一个更好一些。好在哪里?,我么从如下几个方法分析下
首先,咱们从代码的架构考虑,实际上,Runnable 里只有一个 run() 方法,它定义了须要执行的内容,在这种状况下,实现了 Runnable 与 Thread 类的解耦,Thread 类负责线程启动和属性设置等内容,权责分明。
第二点就是在某些状况下能够提升性能,使用继承 Thread 类方式,每次执行一次任务,都须要新建一个独立的线程,执行完任务后线程走到生命周期的尽头被销毁,若是还想执行这个任务,就必须再新建一个继承了 Thread 类的类,若是此时执行的内容比较少,好比只是在 run() 方法里简单打印一行文字,那么它所带来的开销并不大,相比于整个线程从开始建立到执行完毕被销毁,这一系列的操做比 run() 方法打印文字自己带来的开销要大得多,至关于捡了芝麻丢了西瓜,得不偿失。若是咱们使用实现 Runnable 接口的方式,就能够把任务直接传入线程池,使用一些固定的线程来完成任务,不须要每次新建销毁线程,大大下降了性能开销。
第三点好处在于 Java 语言不支持双继承,若是咱们的类一旦继承了 Thread 类,那么它后续就没有办法再继承其余的类,这样一来,若是将来这个类须要继承其余类实现一些功能上的拓展,它就没有办法作到了,至关于限制了代码将来的可拓展性。
综上所述,咱们应该优先选择经过实现 Runnable 接口的方式来建立线程。
学习一点点,努力一点点,分享一点点,你们加油!