java有以下四种创建多线程的方式:
- 1):继承Thread类创建线程
- 2)实现Runnable接口创建线程
- 3)使用Callable和FutureTask创建线程
- 4)使用线程池,例如用Executor框架创建线程
1)继承Thread类创建线程步骤如下:
- 定义一个继承Thread线程类的类,在类中重写run方法。
- 在main函数中实例化这个类,初始化实现r继承Thread类的对象。
- 用这个类的实例调用start方法,执行覆写的run方法。这里是父类即Thread接收创建的对象,由于自身没有start方法,所以是调用父类Thread的start方法创建线程。
2)实现Runnable接口创建线程步骤如下:
- 定义一个实现Runnable接口的类A,在类中重写run方法。
- 在main函数中实例化这个类,但是注意,这不是线程类或者其子类,无法使用start方法,所以无法像1)方法一样用父类接收其对象直接调用start方法执行run方法。
- 实例化线程类,将上一步实例化的Runnable接口的类作为参数传给Thread线程类,初始化线程对象,即 Thread t = new Thread(new A());
- 线程类实例调用start方法执行run方法体。
为什么不直接调用run方法,而是必须先调用start方法呢?
因为start方法会启动线程,此时该线程处于就绪状态,start方法体中有一个start0的native方法,在这个方法里创建了线程,并且执行调用了重写的run方法,在run方法结束时,线程便会中止,也就是start0方法退出时。如果是直接调用run方法,即调用重写的run方法,这和普通方法没有什么区别,不会创建新线程,因为创建新线程,使线程处于就绪状态的操作在start方法中,而run方法会被jvm在start方法中的start0中调用。
start0方法源码解析请看https://www.jianshu.com/p/81a56497e073
这里再多思考一下:
一个线程可以多次调用start方法吗
不能,会抛出下列异常,也就是非法线程状态异常,属于运行异常。查看源码可以看到在执行start时,会对线程状态进行判断,如果线程已经启动过,那么通常情况下无法再次进行启动,这一点在源码注释中也可以看到。即在同一个线程只能start一次,多次调用start方法会抛出异常。当调用start方法时,线程会被添加到线程组中,等待线程调度器调用,当获取到资源时,就进入运行状态。
源码分析请看https://cloud.tencent.com/developer/article/1386458
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at thread.MyThread.main(MyThread.java:13)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
好了我们言归正传,继续看第三种创建线程的方法
3)使用Callable和FutureTask创建线程步骤如下:
- 定义一个Callable接口的实现类。
- 创建Callable实现类对象传递给FutureTask构造器。
- 将FutureTask对象传递给Thread构造器,Thread对象调用start方法启动线程,这一步新线程已经创建完成,处于就绪状态
- 通过FutureTask对象的get方法获取线程运行的结果。
4)使用线程池,例如用Executor框架步骤如下:
- 使用Executors工具类中的静态工厂方法用于创建线程池。
- 创建线程池使用execute方法启动线程。
- 使用shutdown方法等待提交的任务执行完成并后关闭线程。
Runnable和Callable有什么区别?
- Runnable接口定义的run方法,Callable定义的是call方法。
- run方法没有返回值,call方法必须有返回值。
- run方法无法抛出异常,call方法可以抛出checked exception。
- Callable和Runnable都可以应用于executors。而Thread类只支持Runnable.
几种方法优缺点对比:https://blog.csdn.net/sinat_27933301/article/details/69944286
代码示例:
package thread;
import java.util.concurrent.*;
/**
* Created by Administrator on 2019\4\23 0023.
*/
public class MyThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程的第一种方法
Thread1 thread1 = new Thread1();
thread1.start();
// 创建线程的第二种方法
Thread2 thread2 = new Thread2();
Thread thread = new Thread(thread2);
thread.start();
// 创建线程的第三种方法
Callable<String> callable = new Thread3();
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread thread3 = new Thread(futureTask);
thread3.start();
String s = futureTask.get();
System.out.println(s);
// 创建线程的第四种方法
Executor executor = Executors.newFixedThreadPool(5);
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread()+"创建线程的第四种方法");
}
});
((ExecutorService) executor).shutdown();
}
}
class Thread1 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread()+"创建线程的第一种方法");
}
}
class Thread2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread()+"创建线程的第二种方法");
}
}
class Thread3 implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread()+"创建线程的第三种方法";
}
}
运行结果:
可以看出创建了四个线程,而且最后一个线程是线程池所创建。
Thread[Thread-0,5,main]创建线程的第一种方法
Thread[Thread-1,5,main]创建线程的第二种方法
Thread[Thread-2,5,main]创建线程的第三种方法
Thread[pool-1-thread-1,5,main]创建线程的第四种方法