Java并发方面有很多书籍以及博客,针对于线程创建方式有着不同描述,例如实现Runnable接口、集成Thread类、使用线程池工具类以及结合Callable和Future创建线程等。
创建线程的两种方式
Oracle官方文档,即java.lang.Thread类注释的表述是有如下两种创建线程的方式。https://docs.oracle.com/javase/8/docs/api/index.html
方式一:实现Runnable接口,传入Thread类
public class RunnableStyle implements Runnable{
public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
@Override
public void run() {
System.out.println("Runnable接口方式创建线程");
}
}
方式二:继承Thread类,重写run方法
public class ThreadStyle extends Thread {
@Override
public void run() {
System.out.println("用Thread方式创建线程");
}
public static void main(String[] args) {
new ThreadStyle().start();
}
}
两种方式的比较
推荐采用实现Runnable接口,传入Thread类。
-
从代码架构角度:具体执行的任务(run方法)应该和创建/运行线程的机制(Thread类)解耦。
与重写Thread类run方法一样,实现Runnable接口run方法的内容也是具体执行的任务。但是Runnable方式则可以创建单独任务类实现Runnable接口,然后传入任务实例到Thread类中。这样同一个任务类可以传给不同的Thread,同时任务类不需要负责创建线程等工作,因此是解耦的。 -
从继承的角度上,Java是单继承,如果继承了Thread类就无法继承其它类,限制可扩展性。
-
从资源节约的角度上讲,如果是Thread类方式,每次新建一个任务只能新建一个独立的线程,会有额外线程创建/销毁等损耗。而Runnable更加方便采用线程池工具,减少创建、销毁线程带来的损耗。
两种方式本质对比
两种方式在多线程实现的本质上是一致的,都是调用thread对象的start方法创建线程。主要区别在于run方法的内容来源上。
# java.lang.Thread
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
实现Runnable方式,最终调用了Runnable对象target的run方法;而继承Thread方式是重写了整个run方法。
示例:代码同时使用两种方式
匿名内部类实现Runnable接口,并在类内部重写run方法。重写run方法的代码直接覆盖Thread类run方法代码,Runnable中run方法实现代码不会执行。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用Runnable方式创建线程");
}
}) {
@Override
public void run() {
System.out.println("使用Thread方式创建线程");
}
};
thread.start();
多线程的其他代码实现形式
线程池创建线程的方式
public class ThreadPoolStyle {
public static void main(String[] args) {
// 不提倡的创建线程池方式--原因参见阿里规约
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
pool.submit(new Task());
}
}
}
class Task implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
我们通过Executors创建线程池,深入源码可以看到,内部创建ThreadPoolExecutor时会使用ThreadFactory。以DefaultThreadFactory为例,其内部创建线程的方法newThread仍然是通过Runnable创建线程。
## java.util.concurrent.Executors
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;
}
通过Callable和FutureTask创建线程的方式
public class CallableFutureTaskStyle {
public static void main(String[] args) {
// 创建任务,启动线程
FutureTask<String> futureTask = new FutureTask<>(new CallableTask());
new Thread(futureTask).start();
}
}
class CallableTask implements Callable {
@Override
public String call() throws Exception {
return "使用Callable和FutureTask创建线程";
}
}
Callable接口是一个独立的接口,用于创建任务,通常与FutureTask配合使用。而FutureTask的父类是Runnable,因此其本质仍然是Runnable方式创建线程。
定时器创建线程
public class TimerStyle {
public static void main(String[] args) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
},100,100);
}
}
定时器任务类TimerTask实现Runnable接口。
public abstract class TimerTask implements Runnable
匿名内部类和Lambda代码实现形式
多线程的代码实现方式有很多种,但其本质仍然是出于继承Thread和实现Runnable接口两种方式。线程池、Callable以及定时器,对创建线程做了一定的封装,但本质仍然没有变。至于线程匿名内部类和Lambda实现,只是代码实现形式的不同,不能作为实现线程的方式。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
较为准确的描述
(1)首先从不同的角度看,会有不同的答案。例如从代码实现,还是从本质上说。
(2)通常我们可以分为两种,分别是实现Runnable接口和继承Thread类。Thread类的注释也是这样表述的。
(3)描述Runnable方式和Thread方式的不同(3个角度)。
(4)其实Thread类实现了Runnable接口,类中实现了run方法。可以发现两种方式在创建线程的本质上是一样的,都是调用Thread对象的start方法,主要区别在于run方法内容的来源不同:Runnable方式最终是调用Ruannble对象target的run方法,而Thread方式则是使用了重写的run方法。
(5)还有其他实现线程的方式,例如线程池也能新建线程,但是细看源码,其本质是Runnable方式。
(6)准确的讲,创建线程只有一种方式,那就是创建Thread类,而实现线程的run方法有两种方式。除此之外,从表面上看线程池、定时器等工具类也可创建线程,但是本质没有变。
转载:https://blog.csdn.net/LIZHONGPING00/article/details/104118829