Java JUC线程池

简介: Java JUC 线程池



  1. 程池

程序的运行,本质上就是占用系统的资源,我们要做的就是优化资源的使用,所以衍生出一种技术叫:池化技术  线程池 连接池 内存池 对象池,都是类似的概念

什么是池化技术呢?

简单来说,池化技术就是:提前准备好一些资源,就来拿来使用,用完之后还回来,以供下次使用,这种思想可以是CPU效率更高

线程池的好处?

  1. 降低资源的消耗
  2. 提高响应的速度(不用频繁的创建销毁,使用完之后并不会进行销毁,而是返回到线程池,以便下次使用)
  3. 由于把线程都放在线程池中了,可以方便管理

总结来说:线程池的好处就是线程可以复用且方便管理,可以控制最大并发数

线程池该怎么使用呢?

  1. 创建线程池  

Executors.newxxx();

  1. 使用线程池创建线程

execute 使用线程池创建线程使用execute,丢弃之前的new Thread

  1. 关闭线程池 shutDown();

一般配合try catch finally使用 try中写创建线程池和业务,finally中关闭线程池


//创建线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
//使用线程池创建线程
executorService.execute(()->{
            System.out.println(Thread.currentThread().getName() + "ok");
        });
//线程池使用完 使用shutDown()关闭线程池
    executorService.shutdown();
//创建一个单例线程
ExecutorService executorService = Executors.newSingleThreadExecutor();
//使用线程池创建线程的话 就不使用new Thread了  而且使用execute来创建线程
try {
for (int i = 0; i < 10; i++) {
        executorService.execute(()->{
            System.out.println(Thread.currentThread().getName() + "ok");
        });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
  //线程池使用完 使用shutDown()关闭线程池
    executorService.shutdown();
}


简单来说就是:3大方法 7大参数 4种拒绝策略

线程池不允许使用Executors去创建(Executors其实就是个工具类,3大方法都和Executors有关),而是通过ThreadPoolExecutor的方式去创建,这样的处理方式可以更加明确线程池的运行规则,避免资源耗尽的风险(OOM内存溢出)

  3大方法

package com.wyh.ThreadPool;
import java.util.concurrent.Executors;
/**
 * @program: JUC
 * @description: 线程池
 * @author: 魏一鹤
 * @createDate: 2022-02-28 22:14
 **/
public class ThreadPoolDemo01 {
public static void main(String[] args){
        //Executors其实就是个工具类,3大方法都在它里面
        //3大方法 1 newSingleThreadExecutor得到一个单一的线程池 这个线程池只有一个线程处理
        Executors.newSingleThreadExecutor();
        //3大方法 2 newFixedThreadPool得到一个固定的线程池大小  参数就是线程池大小
        Executors.newFixedThreadPool(5);
        //3大方法 3 newFixedThreadPool得到一个缓存的线程池 可以伸缩的 遇强则强遇弱则弱
        Executors.newCachedThreadPool();
    }
}

1 单一的线程池


Executors.newSingleThreadExecutor();  //得到一个单一的线程池 这个线程池只有一个线程处理


package com.wyh.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @program: JUC
 * @description: 线程池
 * @author: 魏一鹤
 * @createDate: 2022-02-28 22:14
 **/
public class ThreadPoolDemo01 {
public static void main(String[] args){
        //创建一个单例线程
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        //使用线程池创建线程的话 就不使用new Thread了  而且使用execute来创建线程
        try {
for (int i = 0; i < 10; i++) {
            //使用线程池创建线程的话 就不使用new Thread了  而且使用execute来创建线程
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
  //线程池使用完 使用shutDown()关闭线程池
            executorService.shutdown();
        }
    }
}


通过结果发现线程池中的线程被一个线程调用 这就是单例线程池



pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok

pool-1-thread-1ok



2 固定的线程池


Executors.newFixedThreadPool(5);  //得到一个固定的线程池大小  参数就是线程池大小


package com.wyh.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @program: JUC
 * @description: 线程池
 * @author: 魏一鹤
 * @createDate: 2022-02-28 22:14
 **/
public class ThreadPoolDemo01 {
public static void main(String[] args){
        //创建一个固定个数的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
//使用线程池创建线程的话 就不使用new Thread了  而且使用execute来创建线程
        try {
for (int i = 0; i < 10; i++) {
            //使用线程池创建线程的话 就不使用new Thread了  而且使用execute来创建线程
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//线程池使用完 使用shutDown()关闭线程池
            executorService.shutdown();
        }
    }
}


pool-1-thread-2ok

pool-1-thread-4ok

pool-1-thread-3ok

pool-1-thread-6ok

pool-1-thread-1ok

pool-1-thread-5ok

pool-1-thread-8ok

pool-1-thread-7ok

pool-1-thread-9ok

pool-1-thread-10ok



通过结果发现线程池中的线程被多个线程并发调用,最多的并发数是我创建线程池时生命的线程个数 这就是固定个数线程池

就算我用10个线程并发执行,但是创建线程池个数就是1,那这10个线程也是被这被一个线程并发执行,这个思想类似于单例线程池,不管并发量多少都是一个线程并发执行




package com.wyh.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @program: JUC
 * @description: 线程池
 * @author: 魏一鹤
 * @createDate: 2022-02-28 22:14
 **/
public class ThreadPoolDemo01 {
public static void main(String[] args){
        ExecutorService executorService = Executors.newFixedThreadPool(1);
try {
for (int i = 0; i < 10; i++) {
//使用线程池创建线程的话 就不使用new Thread了  而且使用execute来创建线程
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//线程池使用完 使用shutDown()关闭线程池
            executorService.shutdown();
        }
    }
}

3 可缓存的线程池


Executors.newCachedThreadPool(); //得到一个缓存的线程池 可以伸缩的 遇强则强遇弱则弱


package com.wyh.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @program: JUC
 * @description: 线程池
 * @author: 魏一鹤
 * @createDate: 2022-02-28 22:14
 **/
public class ThreadPoolDemo01 {
public static void main(String[] args){
            //创建一个可缓存的线程池 会随着线程个数提高并发量 遇强则强遇弱则弱,强弱根据线程数决定
        ExecutorService executorService = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 50; i++) {
//使用线程池创建线程的话 就不使用new Thread了  而且使用execute来创建线程
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//线程池使用完 使用shutDown()关闭线程池
            executorService.shutdown();
        }
    }
}

通过结果发现线程池中的线程并发量会随着线程个数提高并发量 遇强则强遇弱则弱,强弱根据线程数决定,这就是缓存线程池


pool-1-thread-2ok

pool-1-thread-5ok

pool-1-thread-6ok

pool-1-thread-4ok

pool-1-thread-8ok

pool-1-thread-1ok

pool-1-thread-3ok

pool-1-thread-9ok

pool-1-thread-5ok

pool-1-thread-7ok

pool-1-thread-7ok

pool-1-thread-5ok

pool-1-thread-9ok

pool-1-thread-2ok

pool-1-thread-1ok

pool-1-thread-10ok

pool-1-thread-3ok

pool-1-thread-6ok

pool-1-thread-4ok

pool-1-thread-8ok

pool-1-thread-11ok

pool-1-thread-3ok

pool-1-thread-10ok

pool-1-thread-13ok

pool-1-thread-1ok

pool-1-thread-9ok

pool-1-thread-1ok

pool-1-thread-2ok

pool-1-thread-7ok

pool-1-thread-5ok

pool-1-thread-15ok

pool-1-thread-9ok

pool-1-thread-1ok

pool-1-thread-7ok

pool-1-thread-2ok

pool-1-thread-14ok

pool-1-thread-13ok

pool-1-thread-10ok

pool-1-thread-8ok

pool-1-thread-11ok

pool-1-thread-3ok

pool-1-thread-12ok

pool-1-thread-6ok

pool-1-thread-4ok

pool-1-thread-18ok

pool-1-thread-1ok

pool-1-thread-17ok

pool-1-thread-7ok

pool-1-thread-16ok

pool-1-thread-5ok


7大参数

无论是哪种线程池,本质都是调用ThreadPoolExecutor,它有7大参数

  1. corePoolSize    核心线程池大小
  2. maximumPoolSize  最大线程池大小
  3. long keepAliveTime  超时等待 (超时了没有还调用会释放)
  4. TimeUnit unit    超时时间单位
  5. BlockingQueue<Runnable> workQueue  阻塞队列
  6. ThreadFactory threadFactor 线程工厂 创建线程的 一般不用动
  7. RejectedExecutionHandler handler  拒绝策略


//本质 ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize, //核心线程池大小
int maximumPoolSize, //最大线程池大小
long keepAliveTime, //超时等待 (超时了没有还调用会释放)
                          TimeUnit unit, //超时时间单位
                          BlockingQueue<Runnable> workQueue, //阻塞队列
                          ThreadFactory threadFactory, //线程工厂 创建线程的 一般不用动
                          RejectedExecutionHandler handler //拒绝策略) {
if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
            AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}


自定义手动创建线程池


//使用ThreadPoolExecutor自定义线程池 它有7个参数
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,//核心线程池大小 正常只有2个线程处理
        5, //最大线程池大小  并发量大的时候最多处理的线程数
        3, //等待时间  3
        TimeUnit.SECONDS, //时间单位  秒
        new LinkedBlockingDeque<>(3),//阻塞队列  存放的队列数
        Executors.defaultThreadFactory(),//线程工程  Executors的defaultThreadFactory方法默认创建的线程工厂 一般不用变
        new ThreadPoolExecutor.AbortPolicy());//拒绝策略 如果队列满了 还有队列进来 就不处理满了之后进来的队列,并且抛出异常



4种拒绝策略

默认的拒绝策略:Abortpolicy 如果队列满了 还有队列进来 就不处理满了之后进来的队列,并且抛出异常


1 Abortpolicy   如果队列满了 还有队列进来 就不处理满了之后进来的队列,并且抛出异常


2   CallerRunsPolicy 哪来的去哪里(打发)



3 DiscardPolicy  队列满了,丢掉多余的任务 不会抛出异常



4  DiscardOldestPolicy 队列满了,会尝试和旧的队列(最早加入的队列)竞争 也不会抛出异常


总结和扩展:

创建线程池使用的是ThreadPoolExecutor而不是Executros,因为Executros不安全(max=Interger.max)相当于20亿个线程,会造成OOM(内存溢出)异常

知道线程池的3种线程池(3大方法),7大参数,4种拒绝策略

线程池的最大的大小应该如何定义设置?

一般有两种方法

IO密集型和CPU密集型一般用于调优(优化性能)

1 CPU密集型 几核,就把最大线程定义为几,可以保证CPU的效率最高

Runtime.getRuntime().availableProcessors() //获取cpu处理器个数 也叫CPU密集型
//使用ThreadPoolExecutor自定义线程池 它有7个参数
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,//核心线程池大小 正常只有2个线程处理
       //最大线程数一般写电脑的处理器个数 使用CPU密集型   Runtime.getRuntime().availableProcessors() 获取该电脑有多少cpu处理器
        Runtime.getRuntime().availableProcessors(), //最大线程池大小  并发量大的时候最多处理的线程数
        3, //等待时间  3
        TimeUnit.SECONDS, //时间单位  秒
        new LinkedBlockingDeque<>(3),//阻塞队列  存放的队列数
        Executors.defaultThreadFactory(),//线程工程  Executors的defaultThreadFactory方法默认创建的线程工厂 一般不用变
        //拒绝策略
        // 1 AbortPolicy  如果队列满了 还有队列进来 就不处理满了之后进来的队列,并且抛出异常
        //new ThreadPoolExecutor.AbortPolicy()
        // 2 CallerRunsPolicy 哪来的去哪里 new ThreadPoolExecutor.CallerRunsPolicy
        // 3 DiscardPolicy  队列满了,丢掉多余的任务 不会抛出异常   ThreadPoolExecutor.DiscardPolicy()
        // 4 DiscardOldestPolicy 队列满了,会尝试和旧的队列(最早加入的队列)竞争 也不会抛出异常  new ThreadPoolExecutor.DiscardOldestPolicy()
        new ThreadPoolExecutor.DiscardOldestPolicy());

2 IO密集型  判断程序中十分耗费IO的线程,然后定义的最大线程大于耗费IO的线程就可以了,一般定义的最大线程数是IO耗费的两倍

比如一个程序中有15个大型任务,IO任务非常占用资源,至少有15个线程去操作IO任务,那么线程池就可以定义30个最大线程,是IO线程的两倍

目录
相关文章
|
15天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
70 17
|
25天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
10天前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
27天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
27天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
28天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
54 3
|
28天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
150 2
|
1月前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
51 6
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
1月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####

热门文章

最新文章