创建一个线程的方法
创建一个线程的方法,有非常多种:
-
继承
Thread类,重写run方法。 -
实现
Runnable接口,实现run方法,然后将Runnable实例传递给Thread构造器。 -
实现
Callable接口,结合FutureTask和Thread类(这是对Runnable的功能增强,用于获取返回值)。
继承Thread类
创建一个子类继承Thread类,并且重新run方法
csharp
Thread thread=new Thread(){
@Override
public void run() {
while(true){
System.out.println("线程执行中...");
}
}
};
实现Runnable接口
通过实现Runnable接口,重写run方法
csharp
Runnable runnable=new Runnable() {
@Override
public void run() {
while(true){
System.out.println("线程执行中...");
}
}
};
Thread thread=new Thread(runnable);
实现Callable接口,并封装在FutureTask中,再放入Thread
ini
Callable<String> callable=new Callable<>() {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "hello,JUC";
}
};
FutureTask<String> task=new FutureTask<String>(callable);
Thread thread=new Thread(task);
thread.start();
String s = task.get();
System.out.println(s);
Thread源码解析
三种创建线程的相同点
现在来看看Thread的背后做了什么事。
typescript
public class Thread implements Runnable {
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
从上述简化的代码来看,Thread本质也是一个Runnable,并且还持有一个Runnable对象。
因此上面的几种方法都有了解释:
- 第一种继承
Thread,直接覆写run方法的逻辑,不再使用target。 - 第二种传入一个
target,执行target中的run方法 - 第三种的本质也是第二种
线程启动的过程
以下是start的源码
csharp
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}b
}
}
start方法被synchronized修饰,确保在多线程环境下,检查线程状态和启动过程是原子的,防止一个线程对象被并发启动多次。
threadStatus为0时,代表着是NEW状态,此时线程对象创建了,但是没有启动,如果threadStatus不为0,说明线程已经启动了。
通过启动加锁与检查线程状态,保证了线程不可重复启动的特性。
从代码中并没有看到run方法被调用的语句,因为真正的启动在start0中