【多线程】创建线程到底是多少种方法?

🥰🥰🥰来都来了,不妨点个关注叭!
👉博客主页:欢迎各位大佬!👈

在这里插入图片描述

如标题所示,创建线程到底有几种方法?

答案是:不是固定的,可以说有两种,可以说有三种,可以说有四种,可以说有五种…
只要自圆其说即可,可不是瞎说哦 ~

同时,我们可以了解到,在计算机这个奇妙的世界里,很多问题答案并不是固定的,只要我们掌握里面的核心与精髓即可,尤其是像这种问题,根据学习者自己总结的类型,答案肯定是不固定的,每个人的理解不同,归纳的方式不同,所以,理解透彻,掌握核心,才是我们需要做的呀!

下面我们一起看看吧~

1. 创建线程的两种方式总结(最官方)

  • 方式一:继承 Thread 类
  • 方式二:实现 Runnable 接口

通过官方文档 oracle,可以看到,官方给的解释,创建线程的方式是两种,查看方式如下:

在这里插入图片描述
在这里插入图片描述
即为创建新的执行线程有两种方法,一种是将一个类声明为 Thread 的子类,该子类应重写 Thread 类的 run 方法,另一种方法是声明一个实现 Runnable 接口的类,该类实现 run 方法

1.1 继承 Thread 类

class MyThread extends Thread {
    @Override
    public void run() {
        while(true) {
            System.out.println("这是继承Thread创建线程");
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
    }
}

1.2 实现 Runnable 接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        while(true){
            System.out.println("这是实现Runnable接口创建线程");
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t = new Thread(myRunnable);
        t.start();
    }
}

其实本质上来说,创建线程的方式只有一种,就是通过 Thread 类实现,通过上述代码可以直观感受到,只是 Thread 类和 Runnable 接口这两种创建线程方式是通过 run() 方法的位置不同进行区分的。下面的几种创建线程方式的总结,归根到底还是上述的两种,究其本质发现,底层源码仍然是 Thread 类和 Runnable 接口实现的!

1.3 优先考虑使用第二种 —— 实现 Runnable 接口

理由有以下三个方面:

  • 耦合度角度:实现Runnable接口创建线程, Thread线程与run方法耦合开;而继承Thread类创建线程,Thread线程与run方法耦合在一起,因此,优先考虑实现Runnable接口创建线程;
  • 资源角度:每出现一个任务的时候,继承Thread类创建线程,都需要手动创建一个线程,线程的创建和销毁都会消耗比较大的资源,而实现Runnable接口的方式创建线程,仅需实现Runnable接口,将实现类作为参数加入到Thread()中即可,线程Thread通过线程池创建与管理,这样就可以减少线程的创建和销毁,因此,优先考虑实现Runnable接口创建线程;
  • 继承角度:由继承的知识可以知道:Java支持单继承、多层继承、不同类继承同一类,但是不支持多继承即一个子类继承多个父类,因此,一个子类只能继承一个父类,继承Thread类创建线程,采用的是继承的方式,只能继承这一个类,可扩展性大大降低,并且,如果不同类继承同一个Thread类,可能出现父类的run方法被子类重写导致run方法被覆盖的情况,因此,优先考虑实现Runnable接口创建线程。

2. 创建线程的其它方式总结

在这期内容中,介绍了五种创建线程的方式,可回顾这期内容 3.1创建线程 ,Thread类及其基本用法

2.1 创建线程的五种方式总结一

可以看到这期内容介绍的五种创建线程方式如下:

  • 方式一:使用继承Thread,重写run的方式
  • 方式二:使用实现Runnable,重写run的方式
  • 方式三:继承Thread,使用匿名内部类的方式
  • 方式四:实现Runnable类,使用匿名内部类
  • 方式五:lambda表达式(最推荐使用,最简单最直观写法)

显然易见,方式一和二与上面的创建线程的两种方式总结一致,而方式三,其实本质上还是使用继承Thread类,方式四、五本质上还是使用实现Runnable接口,只是使用的形式上是匿名类!

2.2 创建线程的五种方式总结二

  • 方式一:继承Thread类
  • 方式二:实现Runnable接口
  • 方式三:基于lambda
  • 方式四:实现Callable
  • 方式五:线程池创建线程

为什么有人会把基于lambda表达式创建线程的方式进行总结,因为它是最常用的,最直观最简单的写法

这里对线程池创建线程实现Callable创建线程进行介绍:

2.2.1 线程池创建线程

在日常学习或者是开发中,经常通过线程池来创建线程,那么线程池创建线程的本质到底是什么呢?
介绍线程池的文章可回顾这期内容:线程池

ExecutorService pool =  Executors.newFixedThreadPool(10);

在这里插入图片描述
线程池创建线程的方式,本质上来说,还是使用的是实现Runnable接口

2.2.2 实现 Callable 创建线程

与其它方式创建线程不同,实现 Callable 创建线程最大的优点是:可以返回执行完毕后的结果

1)创建任务对象

  • 定义一个类,实现Callable接口,重写call方法,描述线程的任务以及返回后的数据
  • 把Callable类型的对象封装成FutureTask,即线程任务

2)把线程任务对象交给Thread对象
3)使用Thread对象,调用start()方法启动线程
4)线程执行完毕后,通过FutureTask对象get方法获取线程任务执行结果

代码如下:

import java.util.concurrent.Callable;

    //1.MyCallable类实现Callable接口
	public class MyCallable implements Callable<String> {
    	private int n;
    	public MyCallable(int n){
        this.n = n;
    }

    //2.重写call方法
    @Override
    public String call() throws Exception {
        //描述线程的任务以及返回后的数据,求1至n的和并返回结果
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return "线程求出的和为:" + sum;
    }
}
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;


public class ThreadDemo {
    public static void main(String[] args) {
        //1.是一个实现Runnable的对象
        //2.可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果
        //3.创建一个Callable的对象
        Callable<String> call = new MyCallable(10);
        //4.把Callable的对象封装成一个FutureTask对象(任务对象)
        FutureTask<String> f1 = new FutureTask<>(call);
        //5.把任务对象交给一个Thread对象
        Thread t1 = new Thread(f1);
        t1.start();
        //6.获取线程执行完毕后返回的结果
        //如果代码执行到这,上面的线程t1还没有执行完,就会暂停,等待上面线程执行完毕之后才会获取结果
        String ret = null;
        try {
            ret = f1.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(ret);
    }
}

打印的结果为:

在这里插入图片描述
也可以使用匿名内部类的方式,直接重写call方法,代码如下:

public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //只是创建了任务,还没有执行
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i = 1; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };

        //还需要找个人来完成这个任务即线程
        //Thread 不能直接传callable 需要再包装一层

        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        System.out.println(futureTask.get());
        //此处的get就是获取到上述任务call方法返回值的结果
        //调用get与join类似会阻塞等待,并获取到值
    }
}

在这里插入图片描述
实现Callable创建线程的方式,本质上来说,还是使用的是实现Runnable接口

还有小伙伴总结,定时器工具类创建线程,下面进行简单介绍:

2.2.3 定时器工具类创建线程

还记得这个例子嘛!定时器同时定义 3 个任务,按照时间顺序依次执行,可回顾定时器这期内容:定时器

public class ThreadDemo30 {
    public static void main(String[] args) {
        Timer timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello1");
            }
        },1000);


        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello2");
            }
        },2000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello3");
            }
        },3000);
        
    }
}

在这里插入图片描述
实现定时器工具类创建线程的方式,本质上来说,还是使用的是实现Runnable接口

💛💛💛本期内容回顾💛💛💛
在这里插入图片描述
✨✨✨本期内容到此结束啦~

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值