创建线程的方式
1. 继承 Thread 类
在 Java 中,当你启动一个线程时,实际上是调用了 Thread
类的 start()
方法。这个方法会执行以下几个步骤:
- 线程的状态转变:调用
start()
方法后,线程的状态从NEW
转变为RUNNABLE
。这意味着线程已经准备好运行,但并不保证它会立即获得 CPU 时间。 - 调用
run()
方法:当线程获得 CPU 时间后,Java 虚拟机会调用线程的run()
方法。这个方法是你在创建线程时定义的逻辑。在run()
方法中,你可以放置线程需要执行的代码。
public class Main {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("main");
}
// 继承 Thread类
static class MyThread extends Thread {
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程执行了");
}
}
}
那调用 run() 和 调用 start() 有什么区别?
调用 start() 新建一个线程,不会阻塞当前线程,而调用 run() ,当前线程中执行代码,不会创建新线程,当前线程会被阻塞,直到 run()
方法执行完成,不会改变线程的状态,它仍然是 NEW
状态,而不会变为 RUNNABLE
。
2. 实现 Runnable 接口
实现 Runnable 和 Callable 接口,相当于是创建了一个 任务,不算真正意义上的线程,因此还是需要 Thread 进行调用。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
// 实现 Runnable 接口
static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("任务执行");
}
}
}
3. 实现 Callable 接口
可以看出来,Runnable 和 Callable 的区别在于:Runnable 的 run 方法没有返回值,而 Callable 的 call 方法有返回值,可以通过 Future 或者 FutureTask 获取异步任务的执行结果。
注意:调用 FutureTask 的 get() 可以得到执行的结果,但是会阻塞主线程。
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyCallable myCallable = new MyCallable();
FutureTask<String> task = new FutureTask(new MyCallable());
Thread thread = new Thread(task);
thread.start();
// 可以得到执行的结果,但是会阻塞主线程
String result = task.get();
System.out.println(result);
}
static class MyCallable implements Callable {
@Override
public String call() throws Exception {
return "null";
}
}
}