创建多线程可以使用下面四种方式:
- 继承Thread
- 实现Runnable接口
- 通过Callable和FutureTask创建线程
- 通过线程池创建线程
第一印象, 你可能会想到这四种创建线程的方式. 可是其实创建线程只有一种方式, 就是 new Thread()
展示上面所说的四种创建线程的方式:
代码演示
package top.clearlight.blog.hollis.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CreateThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println(Thread.currentThread().getName());
// 第一种创建方式
System.out.println("继承Thread类创建的线程");
Thread t = new SubClassThread();
t.start();
// 第二种创建方式
System.out.println("实现Runnable接口创建线程");
CreateThread t2 = new CreateThread();
new Thread(t2).start();
// 第三种创建方式
CallableThread callableThread = new CallableThread();
FutureTask ft = new FutureTask(callableThread);
new Thread(ft).start();
// get()方法会阻塞主线程, 一直等子线程执行完成并返回后才能继续执行主线程后面的代码
int i = 0;
// isDone判断子线程是否执行完, call方法执行完后, 返回true
while (!ft.isDone()) {
// 子线程执行完执行主线程执行其他事情
System.out.println("main" + i++);
Thread.sleep(1000);
}
System.out.println(ft.get());
System.out.println("阻塞了3s钟后," + Thread.currentThread().getName() + "继续执行");
}
}
class SubClassThread extends Thread {
@Override
public void run() {
System.out.println(getName());
}
}
class CallableThread implements Callable {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000);
return "Hello Thread";
}
}
线程池创建线程代码:
package top.clearlight.blog.hollis.thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CreateThreadPool {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
System.out.println("通过线程池创建线程");
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
/* threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});*/
// 通过线程池创建线程
threadPoolExecutor.execute(() -> System.out.println(Thread.currentThread().getName()));
}
}
首先, 多线程的状态第一步, NEW(初始化) : 新创建一个线程对象, 但还没调用start()方法.
下面开始讲解为何创建线程的方式本质上只有一种!
首先, 扩展Thread, 实际调用父类Thread类的无参构造函数,
进入调用四个参数的构造函数
继续进入(部分代码)
因此, 扩展Thread
方式其实是调用了Thread类的六个构造参数的构造方法来创建线程.
第二种方式 : 实现Runnable
, 通过实现Runnable接口后重写run方法后创建Runnable对象传入接受Runnable接口的构造参数
最终还是进入六个参数的构造函数, 只是传入的参数不同, 其实创建线程的方式, 本质都是通过传入不同的参数来调用同一个构造函数.
第三种方式 :
这里发现, 只是传入的target不同.
第四种方式 : 线程池创建线程
进入execute方法
/*
* 1.如果正在运行的线程少于corePoolSize线程,请尝试执行以下操作:
*以给定命令作为第一个线程来启动新线程
*任务。对addWorker的调用自动检查runState和
* workerCount,因此可以防止假警报的增加
*在不应该执行的情况下通过返回false进行线程化。
*
* 2.如果任务可以成功排队,那么我们仍然需要
*仔细检查我们是否应该添加线程
*(因为现有的自上次检查后死亡)或
*自从进入此方法以来,该池已关闭。所以我们
*重新检查状态,并在必要时回退排队
*停止,如果没有,则启动一个新线程。
*
* 3.如果我们无法将任务排队,那么我们尝试添加一个新的
*线程。如果失败,我们知道我们已经关闭或饱和
*并因此拒绝任务。
* /
调用addWorker
最终还是通过new Thread
方式来创建线程.
对这四种方式进行分析, 可以了解到创建线程的方式其实就只有一种