java多线程创建线程的方式

java创建线程有三种方式

1.`实现Runnable接口
class CreateThread_1   implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
public static void main(String[] args){
        CreateThread_1 createThread = new CreateThread_1();
        Thread thread = new Thread(createThread);
        thread.start();
    }

上面代码用了两个方法,先做解释
currentThread():该方法用于返回当前线程对象
getName():返回该线程的名字

通过上面的代码可知,实现Runnable接口创建线程分为几步
      1.创建实现类实现Runnable接口,实现run方法
      2.创建实现类的实例
      3.将实现类的实例传入Thread构造中
      4.启动线程
因为Runnale是一个函数式接口,所以我们也可以改用java 8 提供的Lambda表达式

    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            System.out.println(Thread.currentThread().getName());
        });
        thread.start();
    }
2. 继承Thread类
public class CreateThread2 extends Thread{
    public static void main(String[] args) {
        CreateThread2 createThread2 = new CreateThread2();
        createThread2.start();
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

继承Thread类创建线程分为:
1.创建自定义类继承Thread类
2.重写Thread类的run方法
3.调用自定义类对象的start方法启动线程

3.实现Callable接口

和实现Runnable接口类似,Java还提供了一种实现Callable的方式来创建线程,那么同样是实现接口,为什么还要提供这样的接口的?
作用:
1.Callable的线程执行体(call方法)是有返回值的
2.call方法可以抛出异常
知道了作用之后,先来看看怎么用?
首先,要明白一个知识点,由于Thread的target属性是Runnable的,而Callable不是Runnable的子接口,那么如何把任务传入,Java提供了Future接口专门用来做这些事情,Future的实现类FutureTask不但实现了Future接口,而且实现了Runnable接口,所以传入任务的时候我们可以用FutureTask包装线程执行任务,传入Thread的构造函数即可。
更为方便的是Future还提供了一些方法让线程的执行变得更加灵活,由于call方法是有返回值的,所以Future提供了get方法来获取线程执行后的返回值,还有一些其他的方法先不做介绍,这里看一下如何使用Callable创建线程。
demo:

public class TestCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    	//包装Callable接口
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("call方法正在执行");
                return "call方法返回";
            }
        });
        //启动线程
        new Thread(futureTask).start();
        //打印执行结果
        System.out.println(futureTask.get());
    }
}

在这里插入图片描述
从这个demo中可以看到Callable创建线程的两个特点,抛出了异常,而且打印了call方法的返回值,这样更像是Runnable接口的升级版。
知道了怎么用,现在看看原理是什么?

  1. 为什么Runnable不能抛出异常,而Callable可以抛出异常?
    子类在实现父类接口方法的时候,抛出异常要小于或者等于父类该方法的异常
    Runnable接口中的run方法是没有抛出异常的,所以后面实现的时候也不能抛出异常,遇到异常只能捕获,看下面源码的截图,是没有抛出异常的。
    在这里插入图片描述
    再来看看Callable接口
    在这里插入图片描述
    call方法抛出了Exception,所以实现该方法什么异常都可以抛出。
  2. 为什么call方法可以返回值的?
    理由很简单,在定义接口方法的时候定义了

在这里插入图片描述
在这里插入图片描述
一个是泛型V,一个是无返回值类型void。
需要注意的是在实现FutureTask的时候是有泛型的,Callable接口实现也是有泛型的,传入泛型就是call方法返回值的类型。

总结:
学习了三种创建线程的方式,分别是:实现Runnable接口,实现Callabe接口,继承Thread类
说一下区别和特点

  1. Callable接口需要重写的是call方法,而Runnable接口和继承Thread类需要重写的是run方法,call方法有返回值,可以抛出异常,run方法没有返回值,只可以捕获异常。
  2. Callable接口和Runnable接口都是实现接口,而继承Thread类需要继承,由于Java的单继承原因,推荐创建线程时实现接口。

猜你喜欢

转载自blog.csdn.net/weixin_42220532/article/details/88925624