阅来越爱之Java多线程(实现Callable接口+线程池+图解)

新增两种创建线程的方式

我们知道在Java中最常见的实现多线程的方式就是继承Thread类和实现Runnable接口,我们总是会根据实际需求来选择相应的实现方式。相比较而言,通过实现Runnable接口来实现多线程更有优势,详细的内容可以看java中的线程(线程\程序\进程、创建线程、图解多线程原理、Thread类中的方法、线程优先级)

但是以上两种方式实现多线程它们的run方法都没有返回值,这有时候就不能满足我们编程中的所需。

一、实现Callable接口

概述

1、实现Callable接口和实现Runnable接口都可以实现多线程,但是实现Runnable接口的run()方法是没有返回值的,而Callable接口可以返回指定类型的值。

2、Callable接口在java.util.consurrent包中,JDK 1.5之后提出。

3、Callable接口本身并不具备执行能力,因此Java提供了一个FutureTask类来使用Callable接口定义的具有返回值的任务。

4、Callable的对象必须先封装到FutureTask中,然后通过FutureTask传给Thread类,这样才能启动多线程。

5、FutureTask的get()方法可以获取到Callable接口实现任务的返回值。

实现Callable接口中各个类之间的关系

首先提出一个问题:我们为什么要把Callable的对象先封装到FutureTask中,然后通过FutureTask传给Thread类?

答:① Callable接口中只有一个方法如下,因此Callable接口没有任何的执行能力。

@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;
}

② 而FutureTask有两个构造方法如下,它可以传入一个Callable类型的对象。

构造方法 功能
FutureTask(Callable callable) 创建一个 FutureTask ,运行时执行给定的 Callable
FutureTask(Runnable runnable, V result) 运行时执行给定的 Runnable ,并使用get返回结果

③ FutureTask实现了RunnableFuture< V >接口,而RunnableFuture< V >接口继承了Runnable, Future< V > ,因此FutureTask的对象可以传给Thread类,并由Thread类调用start()方法执行线程。

它们之间的关系图如下:

在这里插入图片描述

Callable接口的使用

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/*实现Callable<>接口*/
public class MyCallable<String> implements Callable<String> {
    
    
    /*重写call()方法,有返回值*/
    @Override
    public String call() throws Exception {
    
    
        int sum = 0;
        for (int i = 0; i <= 50; i++) {
    
    
            sum = sum+i;
            System.out.println(Thread.currentThread().getName()+":"+sum);
        }
        return (String) (Thread.currentThread().getName()+"计算的50以内的整数和为"+sum);
    }
}

class TestMyCallable{
    
    
    /*这是一个程序入口*/
    public static void main(String[] args) {
    
    
        /*创建实现Callable接口的类的实例化对象*/
        MyCallable<String> myCallable = new MyCallable<>();
        /*创建FutureTask类的实例化对象,并传入Callable接口的实例化对象*/
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        /*创建Thread类的实例化对象,传入FutureTask的实例化对象*/
        Thread thread = new Thread(futureTask,"线程1号");
        /*启动线程*/
        thread.start();
        /*通过FutureTask的get方法获取返回值*/
        try {
    
    
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        }
    }
}

二、使用线程池创建线程

什么情况下需要使用线程池?

当我们经常创建和销毁、使用量特别大的资源时,比如并发情况下的线程。对性能的影响很大。

线程池的创建思路是什么?

提前创建好n个线程,放入到线程池中,使用时获取,使用完之后再放回池子。这样的好处就是避免了频繁创建销毁、实现了重复利用。

线程池描述

线程池本身就是一个容器,这个容器是以集合LinkedList< Thread>为底层实现的,当程序第一次启动的时候,创建了多个线程,保存到一个集合之中,当我们想要使用的时候,就从集合中取出线程使用。
在这里插入图片描述

线程池的使用

Executors

1、java.util.concurrent.Executors:线程池的工厂类,用来创建线程池,返回不同类型的线程池。

2、静态方法

(1)static ExecutorService newFixedThreadPool(int nThreads) 创建一个固定线程数的线程池

参数:int nThreads:创建线程池中包含的线程数量

返回值:ExecutorService接口,返回的是一个ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收,面向接口编程

(2)Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池。

(3)Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池。

(4)Executors.newScheduledThreadPool(int corePoolSize):创建一个线程池,可以安排再给定延迟后运行命令或者定期地执行。

ExecutorService

1、java.util.concurrent.ExecutorService:线程池接口。

2、用来从线程池中获取线程,调用start方法,执行线程任务,submit(线程任务)提交一个线程任务用于执行。

3、关闭线程池地方法:void shutdown()。

线程池的代码实现
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class MyPool {
    
    
    /*这是一个程序入口*/
    public static void main(String[] args) {
    
    
        /*①使用线程池的工厂类Executors提供的静态方法newFixedThreadPool生产一个指定数量的线程池*/
        ExecutorService pool = Executors.newFixedThreadPool(3);
        /*③创建线程任务对象*/
        Thread1 t1 = new Thread1();
        /*③创建线程任务对象*/
        Thread2 t2 = new Thread2();
        /*④调用ExecutorService中的submit()方法,传递线程任务,开启线程*/
        pool.submit(t1);
        pool.submit(t2);
        pool.submit(t1);
        pool.submit(t2);
        pool.submit(t1);
        pool.submit(t2);
        pool.submit(t1);
        pool.submit(t2);
        /*⑤调用ExecutorService中的shutdown()方法销毁线程池*/
        pool.shutdown();
    }
}

/*②创建线程任务类*/
class Thread1 implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+":"+"完成任务!!!");
        }
    }
}

/*②创建线程任务类*/
class Thread2 implements Callable<String> {
    
    

    @Override
    public String call() throws Exception {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+":完成任务!!!");
        }
        return null;
    }
}

猜你喜欢

转载自blog.csdn.net/Lotus_dong/article/details/111564078
今日推荐