java如何创建线程:四种实现方式对比

java如何创建线程一共有四种主流写法,分别是继承Thread类、实现Runnable接口、使用Callable+FutureTask、线程池创建线程,其中前两种基础方式无返回值,Callable可以拿到线程执行结果,线程池适合并发任务量大、频繁创建销毁线程的业务场景;如果你只是简单跑一段异步代码,优先选Runnable,需要获取任务返回结果就用Callable,生产环境尽量直接使用线程池,不要手动newThread。

继承Thread类创建线程

Thread类本身就是线程类,你新建类继承Thread,重写里面的run方法,run方法内写线程要执行的业务逻辑。实例化自定义线程对象之后,调用start方法启动线程,不能直接调用run方法,直接调用run只会当成普通方法执行,不会开启新线程。代码结构简单,但是Java是单继承,这个类继承Thread之后就不能再继承其他父类,类复用性较差。

示例代码:定义MyThread继承Thread,重写publicvoidrun(),在方法内编写打印逻辑,main方法中newMyThread(),调用start()。

实现Runnable接口创建线程

Runnable接口只有一个抽象方法run,没有返回值。自定义类实现Runnable接口,重写run方法,把任务代码写在run内部。创建线程的时候,把Runnable实例传入Thread构造器,再调用start启动。这种方式规避单继承限制,任务和线程解耦,同一个任务对象可以交给多个线程执行,资源共享能力更强,开发中使用频率高于继承Thread。

Java8之后还可以用Lambda表达式简写Runnable,减少单独定义类的代码量,一行代码就可以编写任务逻辑,适合简短的异步任务。

Callable+FutureTask创建线程

Runnable的run方法没有返回值,也不能抛出受检异常,Callable接口的call方法支持返回执行结果,还能抛出异常。FutureTask同时实现Runnable和Future接口,作为中间载体,接收Callable对象,再传入Thread构造器启动线程。任务执行完成后,调用FutureTask的get方法就能获取返回结果,get方法会阻塞当前线程直到任务执行完毕。

这个方法适合需要拿到线程运算结果的场景,get阻塞特性是明显约束,不适合高并发下大量同步等待结果的业务,容易造成主线程阻塞堆积。

线程池创建线程

手动newThread创建线程,每次都会新建操作系统线程,任务大量并发时,频繁创建销毁会消耗大量系统资源,还会出现线程数量不可控的问题。线程池预先创建一批线程,任务提交给线程池,由池内线程复用执行任务。Java提供Executors快速创建线程池,阿里Java开发手册不推荐直接使用Executors,建议通过ThreadPoolExecutor构造函数自定义参数,手动指定核心线程数、最大线程数、队列和拒绝策略,避免无界队列引发内存溢出。

创建方式返回值继承限制推荐场景
继承Thread无有,占用继承位简单测试,少量一次性任务
实现Runnable无无大部分异步任务,业务开发首选基础写法
Callable+FutureTask有无需要获取线程执行返回结果
ThreadPoolExecutor线程池可选无生产环境,大量并发任务

start方法的底层会调用操作系统API申请线程资源,就绪后等待CPU调度,run只是任务载体。

线程创建后不代表立刻运行,进入就绪队列,由操作系统调度器分配CPU时间片之后才会执行run中的代码。

该方法的适用边界是,这几种方式都属于用户态层面的线程封装,Java线程在主流操作系统上会映射为操作系统内核线程,创建线程会占用内核栈内存,单台服务器可创建的线程数量存在上限,不能无限创建。

不要在循环里无限制newThread,并发量大时会触发操作系统的线程资源限制,抛出内存相关异常。

敬慕百科汇集百科知识与游戏文化,带你发现世界的每一个精彩角落。

想要了解更多关于java如何创建线程的文章欢迎访问:百科