版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接 https://blog.csdn.net/weixin_43863007/article/details/88840753
创建一个线程有四种方式:
- 继承Thread类创建线程
- 实现Runnable接口创建线程
- 使用Callable和Future创建线程
- 使用线程池例如用Executor框架
1.继承Thread类创建线程:
创建线程的步骤:
- 定义Thread类的子类,并重写该类的 run() 方法,该方法的方法体就是县城需要完成的任务,run() 方法也成为线程的执行体
- 创建Thread子类的实例,也就是创建了线程对象
- 启动线程,即调用线程的 Start() 方法
代码实例:
//继承Thread类
public class MyThread extends Thread{
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
new MyThread().start();
}
}
2.继承Thread类创建线程:
实现步骤:
- 定义Runnable接口的实现类,一样要重写 run() 方法和Thread中的 run() 方法一样是线程的执行体
- 创建Runnable 实现类的实例,并用这个实例作为Thread的 target 来创建 Thread 对象,这个Thread 对象才是真正的线程对象
- 第三步依然是通过调用线程对象的 start() 方法来启动线程
代码实例:
//实现Runnable接口
public class MyThread2 implements Runnable {
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
3.继承Thread类创建线程:
实现过程:
- 使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
- 使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
- 调用FutureTask对象的get() 方法来获得子线程执行结束后的返回值
代码实例:
public class Main {
public static void main(String[] args){
MyThread3 th=new MyThread3();
//使用Lambda表达式创建Callable对象
//使用FutureTask类来包装Callable对象
FutureTask<Integer> future=new FutureTask<Integer>(
(Callable<Integer>)()->{
return 5;
}
);
//实质上还是以Callable对象来创建并启动线程
new Thread(task,"有返回值的线程").start();
try{
//get()方法会阻塞,直到子线程执行结束才返回
System.out.println("子线程的返回值:"+future.get());
}catch(Exception e){
ex.printStackTrace();
}
}
}
4.使用线程池例如用Executor框架:
Executor框架的最大优点是把任务的提交和执行解耦,要执行的任务的人只需要把Task描述清楚,然后提交即可,这个Task是怎么被执行的,被谁执行的,什么时候执行的提交的人就不用关心了,具体点将,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor)将得到一个Future对象,调用Future对象的get方法等待执行结果就好了,Executor框架的内部使用了线程池机制,它在Java.util.cocurrent包下,通过该框架控制线程的启动,执行和关闭,可以简化并法编程的操作,因此,在Java5之后,通过Executor来启动线程比使用Thread的Start方法更好,出了更易管理,效率更好外,还有关键的一点:有助于避免this逃逸问题–如果我们再构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时肯呢个会访问到初始化了一半的对象用Executor在构造器中