本文是Java多线程系列的第一篇文章,关于线程的基础知识这里不多介绍,在我之前的文章中就已经介绍过了,不熟悉什么是线程的同学可以先去看一下。操作系统-进程与线程
稍微了解过一点Java多线程编程的朋友一定知道,Java创建线程的方式一般有三种:重写Thread类、实现Runnable和实现Callable接口,当然线程池也算是创建线程的一种方式,但那就扯远了,我们暂时从这三种方式聊起。
public class CreateThreadTest {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("do something here...");
}
}
很简单的方式,重写Thread类的demo上面已给出。
public class CreateThreadTest {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("do something here...");
}
}
同样很简单,实现Runnable接口后将MyRunnable对象传给Thread类。
public class CreateThreadTest {
public static void main(String[] args) {
FutureTask<Boolean> result = new FutureTask<>(new MyCallable());
Thread t = new Thread(result);
t.start();
try {
System.out.println(result.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("do something here...");
return true;
}
}
实现Callable接口这种方式与实现Runnable接口的不同之处在于多了一个返回值。
首先需要明确的是,start()
是一个native方法,该方法会启动一个线程,线程执行run()
方法里的内容。与直接执行run()
方法不同的是,start()
会启动一个新的线程,这个线程与主线程并发执行。如果直接执行线程的run()
方法就不会启动新的线程,相当于执行了一个普通方法。
run()
方法作为创建线程的核心,我们这里就来扒一扒源码。
首先是Thread类的run()
方法,代码如下:
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
很显然,Thread类中的run()
方法就是调用Runnable对象的run()
方法,我们再看一下Runnable接口的源码。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Runnable接口很简单,就是一个run()
抽象方法。在Runnable接口上有个@FunctionalInterface
注解,表明可以实现函数式编程。
有没有想过当一个线程既继承了Thread类又实现了Runnable接口会执行哪个run()
方法?
public class CreateThreadTest {
public static void main(String[] args) {
Thread t = new Thread(() -> System.out.println("Runnable method")) {
@Override
public void run() {
System.out.println("Thread method");
}
};
t.start();
}
}
大家觉得上面的代码会怎么执行?答案是执行匿名内部类中的方法,也就是打印Thread method
。回顾一下之前分析的源码就不难知道,在Thread中执行的是target对象的run()
方法,而这里我们在匿名内部类中重写了run()
方法,与原来的父类run()
方法已经没有关系了,自然也不会运行target对象的run()
方法。