目前java创建线程存在4中方式:
1: 通过继承 Thread 类本身;
2: 通过实现 Runnable 接口;
3: 通过 Callable 和 FutureTask 创建线程
4: 使用线程池方式
一、通过继承Thread类,并重写run方法实现线程创建
//方式1:继承Thread实现自定义线程
public class MyThread extends Thread{
//重写父类run方法,实现线程执行逻辑
public void run() {
System.out.println("myThead线程执行了.....");
}
}
public class App {
public static void main(String[] args) {
//创建线程对象
MyThread thread = new MyThread();
//调用start方法启动线程
thread.start();
System.out.println("这是主线程.....");
}
}
1:必须继承Thread父类
2:重写run方法,否则是空实现
3:启动线程是使用start() 方法而不是run(), 如果thread.run(), 仅仅表示对象调用方法,并不是多线程操作。
二、通过实现Runable接口创建线程
//方式2:通过实行Runnable接口创建线程
public class MyRunnable implements Runnable {
//线程执行逻辑
public void run() {
System.out.println("线程:" + Thread.currentThread().getName()+" 执行了...");
}
}
public class App {
public static void main(String[] args) {
//创建接口实现类
MyRunnable run = new MyRunnable();
//参数1:Runnable接口实例
//参数2:线程名字
Thread thread = new Thread(run, "线程1");
//调用start方法启动线程
thread.start();
System.out.println("这是主线程.....");
}
}
注意
1: 必须要实现Runnable接口,重写run方法
2: 线程运行需要借助Thread(Runnable run, String ThreadName) 构造器
3: 如果线程不需要多次运行,可以使用匿名内部类实现
public class App2 {
public static void main(String[] args) {
//参数1:Runnable接口实例
//参数2:线程名字
Thread thread = new Thread(new Runnable(){
//线程执行逻辑
public void run() {
System.out.println("线程:" + Thread.currentThread().getName()+" 执行了...");
}
}, "线程1");
//调用start方法启动线程
thread.start();
System.out.println("这是主线程.....");
}
}
分析
其实方式1是方式2的一种变种,本质上还是方式2, 可以打开Thread源码, 发现Thread类也实现了Runnable接口
public class Thread implements Runnable {...}
当new Thread(Runnable, threadName) 时,底层做了这个操作
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
....
//在Thread类中也维护了一个Runnable 对象
this.target = target;
....
}
当thread调用start方法时,实际上调用本地方法,让操作系统开辟线程
public synchronized void start() {
....
start0();
....
}
private native void start0();
最后回调执行run方法,就发现底层实际上是执行Runnable方法
public void run() {
if (target != null) {
target.run();
}
}
三、通过 Callable 和 FutureTask 创建线程
//方式3:使用Callable跟FuntrueTask方式实现
public class MyCallable implements Callable<String> {
//线程执行逻辑
public String call() throws Exception {
System.out.println("线程:" + Thread.currentThread().getName() + ",执行....");
//5s之后返回结果
Thread.sleep(5000);
return "线程执行完后的返回值";
}
}
public class App3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Callable接口实现类对象
MyCallable callable = new MyCallable();
//创建FutureTask对象
FutureTask ft = new FutureTask(callable);
//将FutureTask对象对象作为参数传入
Thread thread = new Thread(ft, "线程1");
//调用start方法启动线程
thread.start();
//获取线程执行完毕返回的值
System.out.println(ft.get());
System.out.println("这是主线程.....");
}
}
注意
1: 必须要Callable 接口,重写call方法, 接口泛型机试call方法返回值类型
2: 需要与FutrueTask对象协同使用
3: FutrueTask 使用可以与Thread对象协同使用,也可以跟线程池配合使用
4: 可以调用FutrueTask对象中的get方法获取线程执行返回值
分析
方式3跟方式1,方式2最大区别在于方式3线程执行完之后可以通过回到方法get返回线程执行结果。那么,它是怎么做的的呢。
看上图,FutrueTask类实现了Runnable接口,该类也持有一个Callable的属性,目的用于接收传入的Callable对象。同时重写了run方法,在run方法中调用了callable接口的call方法,得到返回,而call方法,就是线程逻辑执行方法。此时定义一个变量outcome保存执行结果,那么完全可以通过get方法线程执行结果。再看会操作代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Callable接口实现类对象
MyCallable callable = new MyCallable();
//创建FutureTask对象
FutureTask ft = new FutureTask(callable);
//将FutureTask对象对象作为参数传入
Thread thread = new Thread(ft, "线程1");
//调用start方法启动线程
thread.start();
//获取线程执行完毕返回的值
System.out.println(ft.get());
System.out.println("这是主线程.....");
}
得到结果,其实方法3也是方式2的一种变种。本质还是一样的。
四、通过线程池方式
可以使用线程池方式来构建线程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
ExecutorService ex=Executors.newFixedThreadPool(5);
for(int i=0;i<5;i++) {
ex.submit(new Runnable() {
@Override
public void run() {
for(int j=0;j<10;j++) {
System.out.println(Thread.currentThread().getName()+j);
}
}
});
}
ex.shutdown();
}
}
线程工作了原理:
从这看,线程池更多是对多个线程进行流程控制,本质还是方式1,方式2或者方式3,并没有离开线程本质。
总结:
1: 如果线程类已经继承了其他类,那么创建线程就需要使用Runnable接口/FutureTask方式
2: 如果需要返回值则需要FutrueTask方式
3: 如果需要使用线程池,可以使用Runnable跟FutrueTask方式。
4: 如果需要多线程管理,使用线程池的方式