一、创建线程的三种方式
1.继承Thread类创建线程类
- 定义Thread类的子类,并重写该类的run(),该run()的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
- 创建Thread()子类的实例,即创建了线程对象
- 调用线程对象的start()方法来启动该线程
public class ThreadDemo01 extends Thread{
public ThreadDemo01(){
// 构造方法
}
public void run(){
//编写自己的线程代码
System.out.println(Thread.currentThread().getName());
}
public static void main(String[]args){
ThreadDemo01 threadDemo01 = new ThreadDemo01();
threadDemo01.setName("我是自定义的线程1");
threadDemo01.start();
System.out.println(Thread.currentThread().toString());
}
}
程序结果:
Thread[main,5,main]
我是自定义的线程1
2.通过Runnable接口创建线程类
- 定义Runnable接口的实现类,并重写接口的run()方法,该方法的方法体同样是线程的线程执行体
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程
- 调用线程对象的start()方法来启动该线程
public class ThreadDemo02{
public static void main(String[]args){
System.out.println(Thread.currentThread().getName());
Thread t1 = new Thread(new MyThread());
t1.start();
}
}
class MyThread implements Runnable{
@Override
public void run(){
Sysytem.out.println(Thread.currentThread.getName()+"-->我是通过实现接口的线程方式");
}
}
程序运行结果:
main
Thread-0->我是通过实现接口的线程实现方式!
3.通过Callable和Future创建线程
- 创建Callable接口的实现类,并实现call()方法,该call()方法作为线程执行体,并且有返回值。
- 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。 - 使用FutureTask对象作为Thread对象的target创建并启动新线程
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
public class ThreadDemo03{
public static void main(String[]args){
Callable<Object> oneCallable = new Tickets<Object>();
FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);
Thread t = new Thread(oneTask);
System.out.println(Thread.currentThread().getName());
t.start();
}
}
class Ticket<Object> implements Callable<Object>{
@Override
public Object call()throws Exception{
System.out.println(Thread.currentThread().getName()+"->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
return null;
}
}
程序运行结果:
main
Thread-0->我是通过实现实现Callable接口通过FutureTask包装器实现的线程
前面两种可以归结为一类:没有返回值,通过重写run(),run()的返回值是void,所以没有办法返回结果。
后面两种可以归结为一类:有返回值,通过Callable接口,就要实现call(),这个方法的返回值是Object,所以返回的结果可以放在Object对象中。
4.通过线程池创建线程
public class ThreadDemo05{
private static int POOL_NUM = 10;//线程池数量
public static void main(String[]args) throw InterruptedException{
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 0;i<POOL_NUM;i++){
RunnaleThread threa = new RunnableThread();
executorService.execute(thread);
}
//关闭线程池
executorService.shutdowm();
}
}
class RunnableThread implements Runnable{
public void run(){
System.out.println("通过线程池方式创建的线程:"+Thread.currentThread().getName()+" ");
}
}
二、创建线程的三种方式的对比
1.采用实现Runnable、Callable接口的方式创建多线程
优点:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现面向对象的思想。
劣势:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
2.使用继承Thread的方式创建多线程
优势:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程
劣势:
线程类已经继承了Thread类,所以不能再继承其他父类。
3.Runnable和Callable的区别
- Callable接口重写的方法是call(),Runnable重写的方法是run()
- Callable的任务执行后可返回值,Runnable的任务执行不可返回值
- call方法可以抛出异常,run方法不可以
- 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
版权声明:本文为weixin_46248981原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。