Java提供了三种创建线程的方法:
- 重写
Thread
类的run()
; - 通过实现
Runnable
接口; - 通过
Callable
和FutureTask
创建线程。
通过 Thread 类创建
在Java中,每一个线程实际的创建和运行都是通过Thread
类。Thread
类的run()
方法就是当前线程的入口点(入口方法)。
在Java中,在运行
main()
方法时,实际上是创建了一个名为main
的线程。而main()
方法则是当前应用程序main
线程的入口点(入口方法)。
直接通过Thread
类的无参构造函数创建Thread
对象,创建出来的线程并没有任何实际的作用。也就是说它的run()
方法并没有执行任何实际的任务。
通过Thread
类直接创建线程,需要重写Thread
的run()
方法。重写方法有两种方式:
-
通过匿名内部类重写
run()
方法:@Slf4j public class CreateThread { public static void main(String[] args) { // 匿名内部类创建线程 Thread thread = new Thread() { @Override public void run() { log.debug("running..."); } }; thread.start(); // 运行线程 log.debug("running..."); }
Thread
类的start()
方法可以用于启动线程。当线程被创建之后,需要手动执行start()
方法,线程才会被真正启动。需要注意的是,每个
thread
对象的start()
方法仅能被执行一次。如果start()
方法被多次执行,start()
会抛出一个IllegalThreadStateException
异常。输出如下:
16:47:40.097 [main] DEBUG CreateThread - running... 16:47:40.097 [Thread-0] DEBUG CreateThread - running...
-
通过继承重写
run()
方法:@Slf4j class MyThread extends Thread { @Override public void run() { log.debug("running..."); } public static void main(String[] args) { new MyThread("my-thread").start(); log.debug("running..."); } }
输出如下:
16:47:40.097 [main] DEBUG MyThread - running... 16:47:40.097 [Thread-0] DEBUG MyThread - running...
PS:
为了更好地区分各线程,可以引入
logback
:<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
接着在项目的
resources
的目录下添加一个logback.xml
:<?xml version="1.0" encoding="UTF-8"?> <configuration xmlns="http://ch.qos.logback/xml/ns/logback" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%date{HH:mm:ss} [%t] %logger - %m%n</pattern> </encoder> </appender> <logger name="c" level="debug" additivity="false"> <appender-ref ref="STDOUT"/> </logger> <root level="ERROR"> <appender-ref ref="STDOUT"/> </root> </configuration>
线程名称
通过Thread
创建线程时,可以为线程设置名称,设置线程的名称有两种方式:
-
通过构造函数:
new Thread("my-thread") { // 设置线程名称 @Override public void run() { /* ... */ } }.start();
-
通过
setName()
方法:Thread thread = new Thread() { @Override public void run() { /* ... */ } }; thread.setName("my-thread"); // 设置线程名称 thread.start();
通过 Runnable 创建
Runnable
是一个接口类,它只包含了run()
这一个接口:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
使用Runnable
创建Thread
,首先需要实现Runnable
接口,然后再将实现的Runnable
对象传递给Thread的构造函数进行实例化。实现Runnable
的方式如下几种:
-
通过匿名内部类实现
run()
方法。 -
通过Lambda表达式直接实现
run()
方法。由于
Runnable
中有且仅有一个接口run()
,并且Runnable
被@FunctionalInterface
注解标注(标注了@FunctionalInterface
的接口类可以直接使用Lambda来实现)。所以Runnable
可以直接通过Lambda表达式来实现。示例如下:
// 实现 Runnable Runnable runnable = () -> log.debug("running..."); // 将 Runnable 传递给 Thread 的构造函数 new Thread(runnable).start();
-
通过创建新的类来实现
Runnable
接口。
当使用
Runnable
来实例化Thread
时,可以在实例化Thread
的同时传递一个String
类型的参数来指定Thread
的名称。例如:new Thread(runnable, "my-thread").start();
使用Runnable
实例化Thread
的好处是,可以实现“线程”(Thread
)和“任务”(要执行的代码,即Runnable
)的分离。
Runnable 和 Thread
实际上,Thread
实现了Runnable
接口:
public class Thread implements Runnable {
private Runnable target;
public Thread() {
// 初始化
init(null, null, "Thread-" + nextThreadNum(), 0);
// 此时 target 为 null, run() 方法没有实际的执行代码
}
public Thread(Runnable target) {
// 初始化
init(null, target, "Thread-" + nextThreadNum(), 0);
// init() 中会将 target 赋给成员变量, 也就是 this.target = target (下同)
}
public Thread(Runnable target, String name) {
// 初始化
init(null, target, name, 0);
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
/* 省略其它... */
}
在Thread
中
通过 Callable 和 FutureTask 创建
Callable
是一个接口类,它和Runnable
一样只包含了一个方法,并且都被@FunctionalInterface
所修饰。只不过Callable
需要实现的不是run()
接口,而是call()
接口。Callable
的定义如下:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
可以看到Callable
需要指定一个泛型V
,而这个泛型V
是作为call()
接口的返回值。
Callable
和Runnable
一样可以使用三种方式实现(此处略)。与Runnable
不同的是,Callable
不是作为Thread
构造方法的参数传入,而是作为FutureTask
的参数传入。
通过Callable
和FutureTask
创建Thread
通常包含以下步骤:
- 实现
Callable
; - 通过
Callable
构造FutureTask
; - 通过
FutureTask
构造Thread
。
由于Callable
被@FunctionalInterface
所修饰,所以可以使用Lambda简化代码。代码示例如下:
FutureTask<Integer> task = new FutureTask<>(() -> {
log.debug("running...");
Thread.sleep(1000);
return 100;
});
new Thread(task).start();
// 获取线程执行结果, 等待返回结果 (阻塞)
log.debug("{}", task.get());
如上所示,通过Callable
和FutureTask
创建Thread
的好处就是,可以使用FutureTask
对象来获取call()
方法的执行结果。
FutureTask
同样有run()
方法。因为FutureTask
实现了RunnableFuture
接口:public class FutureTask<V> implements RunnableFuture<V> { /* ... */ }
而
RunnableFuture
继承了Runnable
接口。所以FutureTask
是被作为Runnable
的一个实现类被传入Thread
的构造函数中。
评论