Java多线程&&线程池

Posted itLaity

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程&&线程池相关的知识,希望对你有一定的参考价值。

生命无罪,健康万岁,我是laity。

我曾七次鄙视自己的灵魂:

第一次,当它本可进取时,却故作谦卑;

第二次,当它在空虚时,用爱欲来填充;

第三次,在困难和容易之间,它选择了容易;

第四次,它犯了错,却借由别人也会犯错来宽慰自己;

第五次,它自由软弱,却把它认为是生命的坚韧;

第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;

第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。

初始化线程的 4 种方式

  • 继承Thread
  • 实现Runnable接口
  • 实现Callable接口 + FutureTask(可以拿到返回值,可以处理异常) jdk1.5
  • 线程池

继承Thread

	public static void main(String[] args) throws ExecutionException, InterruptedException 
        /**
         * 1、继承Thread
         * 2、实现Runnable接口
         * 3、实现Callable接口 + FutureTask(可以拿到返回值,可以处理异常) jdk1.5
         * 4、线程池
         */
        Thread01 thread01 = new Thread01();
        thread01.start(); // 启动线程
    
    public static class Thread01 extends Thread 
        @Override
        public void run() 
            System.out.println("Thread当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println(i);
        
    

实现Runnable接口

	public static void main(String[] args) throws ExecutionException, InterruptedException 
        /**
         * 1、继承Thread
         * 2、实现Runnable接口
         * 3、实现Callable接口 + FutureTask(可以拿到返回值,可以处理异常) jdk1.5
         * 4、线程池
         */
        Runnable01 runnable01 = new Runnable01();
        new Thread(runnable01).start();
    
    public static class Runnable01 implements Runnable 
        @Override
        public void run() 
            System.out.println("Runnable当前线程" + Thread.currentThread().getId());
            int i = 12 / 2;
            System.out.println(i);
        
    

实现Callable接口 + FutureTask

	public static void main(String[] args) throws ExecutionException, InterruptedException 
        /**
         * 1、继承Thread
         * 2、实现Runnable接口
         * 3、实现Callable接口 + FutureTask(可以拿到返回值,可以处理异常) jdk1.5
         * 4、线程池
         */
        FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
        new Thread(futureTask).start();
        // 等待整个线程执行完成,获取的返回结果
        Integer integer = futureTask.get();
        System.out.println("Callable返回结果" + integer);
    

    public static class Callable01 implements Callable<Integer> 
        @Override
        public Integer call() throws Exception 
            System.out.println("Callable当前线程" + Thread.currentThread().getId());
            return 14 / 2;
        
    

线程池

常见的 4 种线程池

这里我使用Executors工具类可以帮我们创建的几种常见线程池

newFixedThreadPool

	public static ExecutorService service = Executors.newFixedThreadPool(10);
	public static void main(String[] args) throws ExecutionException, InterruptedException 
        /**
         * 1、继承Thread
         * 2、实现Runnable接口
         * 3、实现Callable接口 + FutureTask(可以拿到返回值,可以处理异常) jdk1.5
         * 4、线程池
         *      - 为什么用线程池 1、2、3,太浪费资源,尤其是高并发系统;
         *      - 将所有的多线程异步任务都交给线程池执行。
         *      - 降低资源的消耗
         *      - 提高响应速度
         *      - 提高线程的可管理性
         */
        // 给线程池直接提交任务 - 保证当前系统中只有一两个,每个异步任务交给线程池
        // service.submit()
        // service.execute();
        service.execute(new Runnable01());
        // Executors工具类可以帮我们创建的几种常见线程池
        // Executors.newFixedThreadPool()  // 固定大小,core=max,都不可回收;创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    
       public static class Runnable01 implements Runnable 
	       @Override
	       public void run() 
	           System.out.println("Runnable当前线程" + Thread.currentThread().getId());
	           int i = 12 / 2;
	           System.out.println(i);
	       
   

newCachedThreadPool

Executors.newCachedThreadPool() // 核心线程为0,所有线程都可回收;创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

newScheduledThreadPool

Executors.newScheduledThreadPool() // 定时任务线程池;创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor

Executors.newSingleThreadExecutor() // 单线程的线程池,后台从队列中获取任务顺序逐一执行;创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

原生线程池

    public static void main(String[] args) throws ExecutionException, InterruptedException 
        // 原生创建线程池
        /**
         * 七大参数
         *  int corePoolSize,    核心线程数量;线程池,创建好以后就准备就绪的线程数量,就等待来接收异步任务来执行
         *  int maximumPoolSize, 最大线程数量;控制资源
         *  long keepAliveTime,  存活时间;如果当前正在运行的线程数量大于核心线程数量。
         *                       释放空闲的线程资源(maximumPoolSize-corePoolSize),只要线程空闲大于指定的keepAliveTime;
         *  TimeUnit unit,       时间单位
         *  BlockingQueue<Runnable> workQueue,  阻塞队列;如果任务有很多, 大于maximumPoolSize的任务就会放到队列里面。
         *                                      只要有线程空闲,就会去队列里面抽取出新的任务继续执行。
         *                                      new LinkedBlockingQueue<>(); 默认是Integer的最大值
         *  ThreadFactory threadFactory,        线程的创建工厂。
         *  RejectedExecutionHandler handler    如果队列满了,按照我们指定的拒绝策略拒绝执行任务。
         *
         *  工作顺序:
         *  运行流程:
         *      1、线程池创建,准备好 core 数量的核心线程,准备接受任务
         *      2、新的任务进来,用 core 准备好的空闲线程执行。
         *          (1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
         *          (2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
         *          (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
         *          (4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
         *      3、所有的线程创建都是由指定的 factory 创建的。
         */
        // Executors.defaultThreadFactory() 默认的线程工厂
        // new ThreadPoolExecutor.AbortPolicy() 丢弃拒绝策略 可自行查看源码,根据业务需求自行选用。
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 200,
                10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10000),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        executor.execute(new Runnable01());
    
    public static class Runnable01 implements Runnable 
        @Override
        public void run() 
            System.out.println("Runnable当前线程" + Thread.currentThread().getId());
            int i = 12 / 2;
            System.out.println(i);
        
    

个人总结

分析4中创建线程的优缺点

        /**
         * 1、继承Thread
         * 2、实现Runnable接口
         * 3、实现Callable接口 + FutureTask(可以拿到返回值,可以处理异常) jdk1.5
         * 4、线程池
         *      - 为什么用线程池 1、2、3,太浪费资源,尤其是高并发系统;
         *      - 将所有的多线程异步任务都交给线程池执行。
         *      - 降低资源的消耗
         *      - 提高响应速度
         *      - 提高线程的可管理性
         *    1、使用:
         *      1)、Executors 工具类
         *          public static ExecutorService service = Executors.newFixedThreadPool(10);
         *          service.execute(new Runnable01());
         *      2)、new ThreadPoolExecutor(); 原生
         * 区别:
         *      1、2没有返回值:主进程无法获取线程的运算结果。不适合当前场景
         *      1、2、3不能控制资源:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致 服务器资源耗尽。
         * 		4可以,性能稳定。
         */

线程池的七大参数

        /**
         * 七大参数
         *  int corePoolSize,    核心线程数量;线程池,创建好以后就准备就绪的线程数量,就等待来接收异步任务来执行
         *  int maximumPoolSize, 最大线程数量;控制资源
         *  long keepAliveTime,  存活时间;如果当前正在运行的线程数量大于核心线程数量。
         *                       释放空闲的线程资源(maximumPoolSize-corePoolSize),只要线程空闲大于指定的keepAliveTime;
         *  TimeUnit unit,       时间单位
         *  BlockingQueue<Runnable> workQueue,  阻塞队列;如果任务有很多, 大于maximumPoolSize的任务就会放到队列里面。
         *                                      只要有线程空闲,就会去队列里面抽取出新的任务继续执行。
         *                                      new LinkedBlockingQueue<>(); 默认是Integer的最大值
         *  ThreadFactory threadFactory,        线程的创建工厂。
         *  RejectedExecutionHandler handler    如果队列满了,按照我们指定的拒绝策略拒绝执行任务。
         *
         *  工作顺序:
         *  运行流程:
         *      1、线程池创建,准备好 core 数量的核心线程,准备接受任务
         *      2、新的任务进来,用 core 准备好的空闲线程执行。
         *          (1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
         *          (2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
         *          (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
         *          (4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
         *      3、所有的线程创建都是由指定的 factory 创建的。
         */

所有代码

package com.laity.gulimall.search.thread;

import java.util.concurrent.*;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.gulimall.search.thread.ThreadTest
 * @Date: 2022年10月20日 18:13
 * @Description: 线程回顾
 */
public class ThreadTest 
    // 给线程池直接提交任务 - 保证当前系统中只有一两个,每个异步任务交给线程池
    public static ExecutorService service = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException 
        /**
         * 1、继承Thread
         * 2、实现Runnable接口
         * 3、实现Callable接口 + FutureTask(可以拿到返回值,可以处理异常) jdk1.5
         * 4、线程池
         *      - 为什么用线程池 1、2、3,太浪费资源,尤其是高并发系统;
         *      - 将所有的多线程异步任务都交给线程池执行。
         *      - 降低资源的消耗
         *      - 提高响应速度
         *      - 提高线程的可管理性
         *    1、使用:
         *      1)、Executors 工具类
         *          public static ExecutorService service = Executors.newFixedThreadPool(10);
         *          service.execute(new Runnable01());
         *      2)、new ThreadPoolExecutor(); 原生
         * 区别:
         *      1、2没有返回值
         *      1、2、3不能控制资源;4可以,性能稳定。
         */
        Thread01 thread01 = new Thread01();
        thread01.start(); // 启动线程
        Runnable01 runnable01 = new Runnable01();
        new Thread(runnable01).start();
        FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
        new Thread(futureTask).start();
        // 等待整个线程执行完成,获取的返回结果
        Integer integer = futureTask.get();
        System.out.println("Callable返回结果" + integer);
        // 给线程池直接提交任务 - 保证当前系统中只有一两个,每个异步任务交给线程池
        // service.submit()
        // service.execute();
        service.execute(new Runnable01());
        // 原生创建线程池
        /**
         * 七大参数
         *  int corePoolSize,    核心线程数量;线程池,创建好以后就准备就绪的线程数量,就等待来接收异步任务来执行
         *  int maximumPoolSize, 最大线程数量;控制资源
         *  long keepAliveTime,  存活时间;如果当前正在运行的线程数量大于核心线程数量。
         *                       释放空闲的线程资源(maximumPoolSize-corePoolSize),只要线程空闲大于指定的keepAliveTime;
         *  TimeUnit unit,       时间单位
         *  BlockingQueue<Runnable> workQueue,  阻塞队列;如果任务有很多, 大于maximumPoolSize的任务就会放到队列里面。
         *                                      只要有线程空闲,就会去队列里面抽取出新的任务继续执行。
         *                                      new LinkedBlockingQueue<>(); 默认是Integer的最大值
         *  ThreadFactory threadFactory,        线程的创建工厂。
         *  RejectedExecutionHandler handler    如果队列满了,按照我们指定的拒绝策略拒绝执行任务。
         *
         *  工作顺序:
         *  运行流程:
         *      1、线程池创建,准备好 core 数量的核心线程,准备接受任务
         *      2、新的任务进来,用 core 准备好的空闲线程执行。
         *          (1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
         *          (2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
         *          (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
         *          (4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
         *      3、所有的线程创建都是由指定的 factory 创建的。
         */
        // Executors.defaultThreadFactory() 默认的线程工厂
        // new ThreadPoolExecutor.AbortPolicy() 丢弃拒绝策略 可自行查看源码,根据业务需求自行选用。
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 200,
                10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10000),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        executor.execute(new Runnable01());
        // Executors工具类可以帮我们创建的几种常见线程池
        // Executors.newCachedThreadPool() // 核心线程为0,所有线程都可回收;创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
        // Executors.newFixedThreadPool()  // 固定大小,core=max,都不可回收;创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
        // Executors.newScheduledThreadPool() // 定时任务线程池;创建一个定长线程池,支持定时及周期性任务执行。
        // Executors.newSingleThreadExecutor() // 单线程的线程池,后台从队列中获取任务顺序逐一执行;创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

    

    public static class Thread01 extends Thread 
        @Override
        public void run() 
            System.out.println("Thread当前线程" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println(i);
        
    

    public static class Runnable01 implements Runnable 
        @Override
        public void run() 
            System.out.println("Runnable当前线程" + Thread.currentThread().getId());
            int i = 12 / 2;
            System.out.println(i);
        
    

    public static class Callable01 implements Callable<Integer> 
        @Override
        public Integer call() throws Exception 
            System.out.println("Callable当前线程" + Thread.currentThread().getId());
            return 14 / 2;
        
    

以上是关于Java多线程&&线程池的主要内容,如果未能解决你的问题,请参考以下文章

JUC并发编程 共享模式之工具 ThreadPoolExecutor 多线程设计模式 -- 异步模式之工作线程(定义饥饿 & 解决饥饿 & 线程池创建多少线程数目合适)

多线程(进阶篇)

多线程(进阶篇)

定时任务 & 定时线程池 ScheduledThreadPoolExecutor

Java Review - 线程池资源一直不被释放案例&源码分析

Java Review - 并发编程_ThreadPoolExecutor原理&源码剖析