java多线程(七)线程池 —— 创建线程池和(Callable + FutureTask)

文章目录

    • 导读
    • 1. 概述
    • 2. 具体步骤
      • 1. 创建线程池
          • 五种线程池:
      • 2. 创建任务(实现Callable带泛型)
      • 3. 开启任务
      • 4. 关闭线程池
    • 综合代码实例:
      • 实例1:(Callable + Futuretask)操控线程
      • 实例2:开启多个任务线程

导读

本篇文章介绍了如何创建线程池,和使用(Callable + FutureTask)获取线程信息

1. 概述

之前我们在使用多线程都是用Thread的start()来创建启动一个线程,但是在实际开发中,如果每个请求到达就创建一个新线程,开销是相当大的。服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用请求的时间和资源要多的多。而有一些线程要频繁创建和删除,因为该线程并不会存在很长时间,这就造成了比必要的消耗。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。这就引入了线程池概念。

线程池的原理其实就是对多线程的一个管理,为了实现异步机制的一种方法,其实就是多个线程执行多个任务,最终这些线程通过线程池进行管理…不用手动去维护…一次可以处理多个任务,这样就可以迅速的进行相应…比如说一个网站成为了热点网站,那么对于大量的点击量,就必须要对每一次的点击做出迅速的处理,这样才能达到更好的交互效果…这样就需要多个线程去处理这些请求,以便能够更好的提供服务…

在Java1.5中提供了一个非常高效实用的多线程包:java.util.concurrent,提供了大量高级工具,可以帮助开发者编写高效易维护、结构清晰的Java多线程程序。
在java.util.concurrent包下,提供了一系列与线程池相关的类。合理的使用线程池,可以带来多个好处:
(1) 降低资源消耗。
(2) 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行;
(3) 提高线程的可管理性。进行统一管理和优化

2. 具体步骤

1. 创建线程池

五种线程池:

ExecutorService service1 = Executors.newSingleThreadExecutor();
一个单线程的线程池,可以用于需要保证顺序执行的场景,并且只有一个线程在执行。表示在任意的时间段内,线程池中只有一个线程在工作

ExecutorService service3 = Executors.newFixedThreadPool(10);一个固定大小的线程池,可以用于已知并发压力的情况下,对线程数做限制。固定型线程池,和newCacheThreadPool()差不多,也能够实现resue(复用),但是这个池子规定了线程的最大数量,也就是说当池子有空闲时,那么新的任务将会在空闲线程中被执行,一旦线程池内的线程都在进行工作,那么新的任务就必须等待线程池有空闲的时候才能够进入线程池,其他的任务继续排队等待.这类池子没有规定其空闲的时间到底有多长.这一类的池子更适用于服务器.

ExecutorService service2 = Executors.newCacheThreadPool();一个可以无限扩大的线程池,比较适合处理执行时间比较小的任务。缓存线程池,先查看线程池中是否有当前执行线程的缓存,如果有就resue(复用),如果没有,那么需要创建一个线程来完成当前的调用.并且这类线程池只能完成一些生存期很短的一些任务.并且这类线程池内部规定能resue(复用)的线程,空闲的时间不能超过60s,一旦超过了60s,就会被移出线程池

ExecutorService service4 = Executors.newScheduledThreadPool(10);可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。调度型线程池,调度型线程池会根据Scheduled(任务列表)进行延迟执行,或者是进行周期性的执行.适用于一些周期性的工作.

newWorkStealingPool:一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行。
代码实例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolTest {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService service = Executors.newCachedThreadPool();
        //匿名内部类创建一个线程
        service.submit(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello world !");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        System.out.println(" ===> main Thread execute here ! ");
    }
}
/*
 ===> main Thread execute here ! 
hello world !
hello world !
hello world !
 */

2. 创建任务(实现Callable带泛型)

可以有返回值。

// 继承Callable接口,泛型参数为要返回的类型
class TaskCallable implements Callable<Integer> {

    //重写call方法类似于run方法执行任务
    @Override
    public Integer call() throws Exception {
        System.out.println("[" + Thread.currentThread().getName() + "] is running");
        Thread.sleep(3000);
        int result = 0;
        for (int i = 0; i < 100; ++i) {
            result += i;
        }
        return result;
    }
}

3. 开启任务

        //1. 创建线程池类
        ExecutorService executor = Executors.newCachedThreadPool();
        //2. 创建实现任务对象
        TaskCallable taskCallable = new TaskCallable();
        //3. 新建FutureTask,将线程对象传入
        FutureTask<Integer> futureTask = new FutureTask<>(taskCallable);
        //4. 使用submit开启futureTask
        executor.submit(futureTask);

4. 关闭线程池

线程池使用完毕,需要对其进行关闭,有两种方法
shutdown()
说明:shutdown并不是直接关闭线程池,而是不再接受新的任务…如果线程池内有任务,那么把这些任务执行完毕后,关闭线程池
shutdownNow()
说明:这个方法表示不再接受新的任务,并把任务队列中的任务直接移出掉,如果有正在执行的,尝试进行停止
Future 介绍
Future的cancel方法可以取消任务的执行,它有一布尔参数,参数为 true 表示立即中断任务的执行,参数为 false 表示允许正在运行的任务运行完成。Future的 get 方法等待计算完成,获取计算结果。Future表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。Future是个借口他的实现类是FutureTask。

综合代码实例:

实例1:(Callable + Futuretask)操控线程

import java.util.concurrent.*;

// 继承Callable接口,泛型参数为要返回的类型
class TaskCallable implements Callable<Integer> {

    //重写call方法类似于run方法执行任务
    @Override
    public Integer call() throws Exception {
        System.out.println("[" + Thread.currentThread().getName() + "] is running");
        Thread.sleep(3000);
        int result = 0;
        for (int i = 0; i < 100; ++i) {
            result += i;
        }
        return result;
    }
}

public class CreateThreadWithCallable {

    public static void main(String[] args) {
        //1. 创建线程池类
        ExecutorService executor = Executors.newCachedThreadPool();
        //2. 创建实现任务对象
        TaskCallable taskCallable = new TaskCallable();
        //3. 新建FutureTask,将线程对象传入
        FutureTask<Integer> futureTask = new FutureTask<>(taskCallable);
        //4. 使用submit开启futureTask
        executor.submit(futureTask);

        System.out.println("[" + Thread.currentThread().getName() + "] is running");

        // 可以调用isDone()判断任务线程状态:是否结束
        if (!futureTask.isDone()) {//如果线程在运行
            System.out.println("[" + Thread.currentThread().getName() + "]:  Task is not done");
            try {//等待让任务线程运行完,不然主线程先跑完了就得不到返回结果
                Thread.sleep(3100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //等线程运行完了,得到返回值
        if (futureTask.isDone()) {
            int result = 0;
            // 因为有返回值,所以可以调用futureTask.get()方法获取任务结果,如果任务没有执行完成则阻塞等待
            try {
                result = futureTask.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("[" + Thread.currentThread().getName() + "]:  Task is done:" + "result is " + result);
            executor.shutdown();
        }

    }
}
/*
[main] is running
[pool-1-thread-1] is running
[main]:  Task is not done
[main]:  Task is done:result is 4950
 */


也可以使用Future来但是使用FutureTask更简洁,因为Future只是一个接口而本质上还是通过FutureTask实例化,所以没有本质区别
但是FutureTask本身就是对象,直接调用相关方法得到结果。
FutureTask传入的参数可以是实现Callable或者Runnable的对象

/*
 * Future + ExecutorService
 * Task task = new Task();
 * ExecutorService service = Executors.newCachedThreadPool();
 * Future future = service.submit(task1);
 * service.shutdown();
 */

实例2:开启多个任务线程

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

//创建线程类实现Callable
class MyCallable implements Callable<String> {
    private int flag = 0;//定义标记

    //构造方法初始化flag
    public MyCallable(int flag) {
        this.flag = flag;
    }

    @Override
    //类似run方法,执行代码,但是可以有返回值
    public String call() throws Exception {
        //如果flag为0则直接返回
        if (this.flag == 0) {
            return "flag = 0";
        }
        //如果flag为1则执行线程任务
        else if (this.flag == 1) {
            try {
                while (true) {
                    System.out.println(Thread.currentThread().getName() + "is running...");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                //捕捉中断异常被cancel了
                System.out.println(Thread.currentThread().getName() + " is Interrupted");
            }
            //返回一个字符串
            return "false";
        } else {//如果flag为其他值,则可以抛出一个异常
            throw new Exception("Bad flag value!");
        }
    }
}

public class ThreadPoolTest {

    public static void main(String[] args) {
        // 创建线程池
        ExecutorService es = Executors.newFixedThreadPool(3);
        //创建FutureTask对象,并传入实现Callable的对象
        FutureTask<String> futureTask1 = new FutureTask<>(new MyCallable(0));
        FutureTask<String> futureTask2 = new FutureTask<>(new MyCallable(1));
        FutureTask<String> futureTask3 = new FutureTask<>(new MyCallable(2));
        //开启任务
        es.submit(futureTask1);//注意submit 一定要是futuretask对象
        es.submit(futureTask2);
        es.submit(futureTask3);
        //此方法关闭线程池但是线程池开启的任务会继续执行直到执行完毕
        es.shutdown();
        
        try {
            //获取第一个任务输出:调用get方法会阻塞等返回值
            System.out.println("task1: " + futureTask1.get());
            //停5秒之后,向任务线程2强制cancel
            Thread.sleep(5000);
            System.out.println("task2 cancel: " + futureTask2.cancel(true));
            // 获取第三个任务的输出,但是没有输出,因为执行第三个任务会引起异常,直接进入catch
            System.out.println("task3: " + futureTask3.get());
        } catch (Exception e) {
            System.out.println(e.toString());
        }


    }
}
/*
pool-1-thread-2is running...
task1: flag = 0
pool-1-thread-2is running...
pool-1-thread-2is running...
pool-1-thread-2is running...
pool-1-thread-2is running...
task2 cancel: true
pool-1-thread-2 is Interrupted
java.util.concurrent.ExecutionException: java.lang.Exception: Bad flag value!
 */

这里当我们会发现当我们想要返回值并进行操作的时候,FutureTask对象可以很容易帮我们实现这个功能。FutureTask可以更方便的操控各个线程。

本章参考代码:java线程池使用
个人做了一些修改和添加了一些注释。

你可能感兴趣的:(Java多线程学习笔记)