创建多线程可以使用下面四种方式:

  • 继承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类的无参构造函数,

创建线程为何只有一种方式? - Java_创建线程


进入调用四个参数的构造函数

创建线程为何只有一种方式? - Java_创建线程_02


继续进入(部分代码)

创建线程为何只有一种方式? - Java_创建线程_03


因此, ​​扩展Thread​​方式其实是调用了Thread类的六个构造参数的构造方法来创建线程.

第二种方式 : ​​实现Runnable​​, 通过实现Runnable接口后重写run方法后创建Runnable对象传入接受Runnable接口的构造参数

创建线程为何只有一种方式? - Java_线程池_04


创建线程为何只有一种方式? - Java_java_05

最终还是进入六个参数的构造函数, 只是传入的参数不同, 其实创建线程的方式, 本质都是通过传入不同的参数来调用同一个构造函数.

第三种方式 :

创建线程为何只有一种方式? - Java_Java_06


创建线程为何只有一种方式? - Java_创建线程_07


这里发现, 只是传入的target不同.

第四种方式 : 线程池创建线程

创建线程为何只有一种方式? - Java_Java_08

进入execute方法

创建线程为何只有一种方式? - Java_线程池_09

/*
* 1.如果正在运行的线程少于corePoolSize线程,请尝试执行以下操作:
*以给定命令作为第一个线程来启动新线程
*任务。对addWorker的调用自动检查runState和
* workerCount,因此可以防止假警报的增加
*在不应该执行的情况下通过返回false进行线程化。
*
* 2.如果任务可以成功排队,那么我们仍然需要
*仔细检查我们是否应该添加线程
*(因为现有的自上次检查后死亡)或
*自从进入此方法以来,该池已关闭。所以我们
*重新检查状态,并在必要时回退排队
*停止,如果没有,则启动一个新线程。
*
* 3.如果我们无法将任务排队,那么我们尝试添加一个新的
*线程。如果失败,我们知道我们已经关闭或饱和
*并因此拒绝任务。
* /

调用​​addWorker​

创建线程为何只有一种方式? - Java_java_10


创建线程为何只有一种方式? - Java_Java_11


创建线程为何只有一种方式? - Java_java_12


最终还是通过​​new Thread​​方式来创建线程.

对这四种方式进行分析, 可以了解到创建线程的方式其实就只有一种