创建线程的几种方式

方式一、直接使用Thread类

无返回结果:继承Thread类,重写run(); 

public class UseThread extends Thread {
    @Override
    public void run() {
        super.run();
        // do my work;
        System.out.println("I am extends Thread");
    }

    public static void main(String[] args) throws Exception {
        UseThread useThread = new UseThread();
        useThread.start();
      //useThread.start();//重复启动报错
}

方式二、使用Runnable接口,配合Thread(受欢迎)

        把【线程】和【任务】(要执行的代码) 分开。

    【Thread】:代表线程。

    【Runnable】:要运行的任务(线程要执行的代码)。

无返回结果: 实现Runnable接口,重写run(); 

@FunctionalInterface
public interface Runnable {
    
    public abstract void run();
}

实现Runnable接口

public class UseRunnable implements Runnable{

    @Override
    public void run() {
        // do my work;
        System.out.println("I am implements Runnable");
    }
}

 java8 lamda 实现Runnable(只有一个方法)接口的写法:

写法:(参数)-> {方法体}

 Runnable  run = () -> {

     System.out.println("I am implements Runnable");

}

 非阻塞调用:

public static void main(String[] args) throws Exception {
     Thread useRunnable = new Thread(new UseRunnable());
     useRunnable.start();
     System.out.println("in main");
}

输出结果:   可以看到useRunnable线程的运行没有阻塞当前线程.

in main

I am implements Runnable

阻塞调用

public static void main(String[] args) throws Exception {
     Thread useRunnable = new Thread(new useRunnable ());
     useRunnable.start();
     useRunnable.join();
     System.out.println("in main");
}

输出结果: Join会阻塞当前线程,一直等待自定义线程执行完成,才返回。

I am implements Runnable
in main

方式三、实现Callable接口 或者叫 FutureTask配合Thread

   JDK1.8中FutureTask实现了RunnableFuture,而RunnableFuture顾名思义就是Runnable接口和Future接口的结合体。因此,FutureTask对象可以作为Runnable对象来用。

FutureTask能够接收Callable类型的参数,用来处理有返回结果的情况。

有返回结果: 
     1. 实现Callable接口,重写call(),利用FutureTask包装Callable,并作为task传入Thread构造函数; 
     2. 利用线程池;

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

在Runnable的例子中,Runnable接口有一个很大的缺陷就是run方法没有返回值定义,主线程无法获取到线程执行的结果。这个时候就需要Callable接口。

Callable、Runnable区别:

        1、Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值;
        2、Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常。

利用Callable接口创建并启动新线程的步骤: 
① 定义CallableDemo 实现Callable接口;Class CallableDemo implements Callable<> 
② 重写call(),将执行的代码写入; 
③ 创建FutureTask的对象;FutureTask中定义了run(),run()内部调用了call(),并保存了call()的返回值;FutureTask<> futuretask = new FutureTask(CallableDemo()); 
④ 创建Thread的对象;Thread thread = new Thread(futuretask);//传入参数Runnable接口 
⑤ 启动线程;thread.start();
⑥ 可通过FutureTask类的get()方法获得线程执行结束后的返回值,即call的返回值。

通过线程池来创建并启动新线程的步骤:
① 定义CallableDemo 实现Callable接口;Class CallableDemo implements Callable<> 
② 重写call(),将执行的代码写入;
③ 定义线程池 :ThreadPoolExecutor executor 
④ 利用submit()方法提交任务 :Future<?> future = executor.submit(new CallableDemo()); 
⑤ 利用FutureTask类get()方法获取返回值.
这里future申明为Future对象,但是它是由FutureTask实现的,也可以直接申明为FutureTask future:

示例代码如下:

public class CallableDemo implements Callable<CallableDto> {
    public CallableDto call() throws Exception {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("in callable demo");
        return new CallableDto(1);
    }
}

class CallableDto {
    private int id;

    public CallableDto(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

非阻塞调用

public static void main(String[] args) throws Exception {
     ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
     Future<CallableDto> future = executor.submit(new CallableDemo());
     System.out.println("in main");
}

输出结果,如下所示,新启动的线程没有阻塞当前线程

in main
in callable demo

阻塞调用,且拿到结果

public static void main(String[] args) throws Exception {
    // 使用方式1:
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
    Future<CallableDto> future = executor.submit(new CallableDemo());
    CallableDto callableDto = future.get();
    System.out.println("in main");
    System.out.println("id from callable is " + callableDto.getId());
    executor.shutDown();// 关闭线程池

    System.out.println("------------------------------------------------------");

   // 使用方式2:实现Callable接口需要用FutureTask进行包装,因为FutureTask实现了Runable接口和Future接口
    CallableDemo callThread = new CallableDemo();
    FutureTask<CallableDto> futureTask = new FutureTask<>(callThread);
    new Thread(futureTask).start();
    CallableDto callableDto2 = futureTask.get();
    System.out.println("2:in main");
    System.out.println("2:id from callable is " + callableDto2.getId());
}
}

输出结果:get方法首先会阻塞主线程,等待当前线程执行结束才返回,且返回线程的执行结果。

in callable demo
in main
id from callable is 1

方式四:CompletableFuture方式

CompletableFuture是jdk1.8引入的api,做了进一步的封装,用户线程无需实现Callable接口也能启动,且能够返回用户线程的执行结果。

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) 

一个没有实现Callable的普通方法

public class CompletableFutureDemo {
    public CompletableFutureDemoDto action() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("in CompletableFutureDemo ");
        return new CompletableFutureDemoDto(1);
    }
}

class CompletableFutureDemoDto {
    private int id;

    public CompletableFutureDemoDto(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

非阻塞调用

public static void main(String[] args) throws Exception {
   ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
   CompletableFuture<CompletableFutureDemoDto> future = CompletableFuture.supplyAsync(() ->
   {
       return new CompletableFutureDemo().action();
   }, executor);
   System.out.println("in main");
}

执行结果: 可以看到,主线程没有被阻塞

in main
in CompletableFutureDemo 

阻塞调用且获取结果

public static void main(String[] args) throws Exception {
   ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
   CompletableFuture<CompletableFutureDemoDto> future = CompletableFuture.supplyAsync(() ->
    {
        return new CompletableFutureDemo().action();
    }, executor);
    CompletableFutureDemoDto demoDto=future.join();
    System.out.println("in main");
    System.out.println("id from demoDto is " + demoDto.getId());
}

执行结果: 主线程一直被阻塞,一直等到用户线程返回

in CompletableFutureDemo 
in main
id from demoDto is 1

总结: 

线程的创建有四种方式:主要分为有返回和无返回,具体根据使用场景来选择。 
1. 如果不需要返回且线程数量小,则建议采用实现Runnable接口,重写run()的方式; 
2. 如果需要返回且线程数量小,则建议采用实现Callable接口,重写call(),利用FutureTask包装成一个Runnable,再作为参数传入Thread的构造方法的方式创建线程; 
3. 如果线程数量较多,则建议采用线程池方式创建:execute提交任务实现无返回操作,submit提交任务实现有返回操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值