Java多线程5---线程优化:线程池、锁优化

一、线程池

1、线程池优点

  (1)提前创建多个线程放在线程池,使用时直接获取,使用完放回池中待用

            避免频繁创建销毁线程的内存资源消耗,实现线程的重复利用。

  (2)提高响应速度---减少了创建新线程的时间

  (3)便于线程的管理(容器思想)

2、线程池的具体实现--executorService  exexutor  

(1)通过exexutors调用newFixedThreadPool创建固定大小的线程池对象,         

          ExecutorService  类型的对象

(2)通过该对象调用execute()方法,将要启动的线程放入线程池

(3)关闭线程池--shutdown()

//1、创建最大线程数为10的线程服务,线程池
ExecutorService  threadServices=Executor.newFixedThreadPool(10);
//2、执行,添加线程,添加线程数为当前线程池内线程数
 threadServices.execute(new MyThread());
 threadServices.execute(new MyThread());
 threadServices.execute(new MyThread());
//3、关闭连接
 threadServices.shutdown();

【注】    

  • 创建线程池的几种方法(静态工厂方法)

(1)newFixedThreadPool   固定大小的线程池 ---核心线程数也是最大线程数,不存在空闲线程,阻塞队列:LinkedBlockingQueue【快】

(2)newSingleThreadExecutor  单线程 ---适用于保证顺序执行任务的场景【慢】

(3)newCachedThreadPool  可伸缩线程池---最大线程数为integer最大值(2^31-1),阻塞队列:SynchoronousQueue【最快】

(4)newScheduleThreadPool  ---最大线程数为integer最大值,不会回收工作线程,容易出现OOM

(5)newworkStealingPool ---jdk8新引入,创建持有足够线程的线程池支持给定的并行度, 通过多个队列减少竞争。

  • 线程池启动线程的两种方式

  service.execute(new 线程)----该线程是继承runnable接口的,没有返回值

  service.submit(new 线程)----该线程是继承Callable接口的,有返回值

  • 关闭线程池

   shutdown---线程池的状态设为SHUTDOWN

                       中断没有正在执行任务的线程,不中断未完成的线程

   shutdownNow--线程状态改为STOP

                      尝试停止正在执行或暂停任务的线程,并返回等待执行任务列表,中断未完成的线程

3、线程池的核心参数

  •  核心线程数--corePoolSize--目前线程池中存在的线程数(长时间稳定存活的线程数)

                                                    线程数超过此数时,会通过线程空闲时间进行线程销毁

  • 工作队列--workQueue--请求>核心线程数部分,放入工作队列等待
  • 最大线程数--maxmumPoolSize--线程池允许的最多线程数
  • 拒绝策略--handler--当请求量>最大线程数时,拒绝请求,选择合适的拒绝策略
  • 线程空闲时间--keep AliveTime---达到某个值被销毁,避免浪费内存资源

  当线程数<核心线程数---创建新线程

  当请求数>工作队列数--- 创建新线程   

  当请求数>核心线程数----线程进入阻塞队列

  当线程数>核心线程数---按照线程空闲时间销毁线程

4、线程池执行过程


 每当有新的任务到线程池时,
(1) 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理,若当前线程数量已达到corePoolSize,则进入下一步;
(2) 判断工作队列(workQueue)是否已满,未满则将新的任务提交到工作队列中,满了则进入下一步;
(3) 判断线程池中的线程数量是否达到了maxumunPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。

 【注】

     在线程池中的线程数量超过corePoolSize时,每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。

    corePoolSize:核心线程数----Java线程池中会长期保持corePoolSize个线程

    maximunPoolSize:线程池最大线程数

参考:Java线程池核心线程数与最大线程数的区别_July的博客-CSDN博客_核心线程数和最大线程数

5、线程池的选择策略

  cpu密集型任务---切换频繁快---线程池

  IO密集型任务---不是一直执行任务---线程池

  依赖其他资源,如数据库连接池---等待时间久---线程池

  优先级不同的任务---优先级队列---priorityBlockingQueue

6、拒绝策略

  AbortPolicy---丢弃任务并抛出异常( 默认使用)【丢弃+异常】

  CallerRunsPolicy---重新尝试提交该任务【重新提交】

  DiscardOldestPolicy---抛弃队列里等待最久的任务,并把当前任务加入队列【最久+队列】

  DiscardPolicy---直接抛弃当前任务,但不抛异常【直接丢弃】

7、阻塞队列(工作队列)

阻塞队列相当于一个缓冲区,生产者和消费者可以以此来通信

常见阻塞队列:

  •  ArrayBlockingQueue 基于数组的有界阻塞队列

      即队列有界,其内部实现是将对象放到一个数组里

  • LinkedBlockingQueue 基于链表的有界阻塞队列

     队列默认最大长度为Integer最大值 2^31-1

  • LinkedTransferQueue 基于链表的无界阻塞队列

     tryTransfer()--若当前有消费者正在等待接收元素,可将生产者传入的元素立刻传输给消费者

    Transfer()--试探生产者传入的元素能否直接传给消费者,无消费者等待,则返回false

区别:transfer:物理消费者是否消费都会立即返回(机制有点类似消息队列的推拉模式)

  • LinkedBlockingDeque 基于链表的双向阻塞队列

    队列两端插入和移出元素,多线程同时入队时减少竞争

  • DelayBlockingQueue 延时获取元素无界阻塞队列

     对元素持有直到一个特定的延迟到期

    getDelay():获取元素延期值,0或负值---过期,正值---需延期或等待清除的时间

  • PriorityBlockingQueue  具体优先级的无界阻塞队列 

     无界并发队列

    具有排序规则,无法插入null值,插入到该队列的元素必须实现Comparable接口

  • SynchronousQueue 同步队列

   每个put必须等待一个take  排队进行 

此部分参考:

JUC集合: BlockingQueue详解 | Java 全栈知识体系

待补充

【注】线程优化的重点:选择合适的线程池,并设置合适的核心参数

           关注响应时间、线程性能、线程和内存使用情况等方面

二、锁优化

(一)锁优化思路

   1.减少锁的持有时间(对需要同步的几行代码进行加锁)

    2.减少锁的粒度 (ConcurrentHashMap采取对segment加锁而不是整个map加锁)

    3.锁分离(将锁划分为读锁和写锁,相互不互斥)

    4.锁粗化(一个间隔性地需要执行同步语句的线程中,如果在不连续的同步块间频繁加锁解锁是很耗性能的,因此把加锁范围扩大总体来说是优化的)

   此部分来源:java多线程锁的优化策略 - 简书

(二)JVM锁优化

1、自旋锁

   通过忙循环形式自旋等待,而不是对线程频繁挂起


2、自适应自旋


    自适应自旋意味着自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。


3、锁清除

    将不可能存在共享数据竞争的锁进行消除。

4、锁粗化

    如果虚拟机探测到有一系列连续操作都对同一个对象反复加锁和解锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部。

5、锁升级

  偏向锁(无CAS)---->轻量级锁(CAS+自旋等待)----->重量级锁(CAS+阻塞队列)


此部分详见锁机制部分:(1条消息) Java多线程2---线程同步和异步、线程安全、锁机制_@snow'的博客-CSDN博客

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线线程池是一种重用线程的机制,它可以管理和调度多个线程来执行任务。使用线程池可以提高程序的性能和效率,同时也可以避免线程的频繁创建和销毁。 在Java中,可以使用线程池类ExecutorService和Executors来创建和管理线程池。具体步骤如下: 1. 创建一个线程池对象:可以使用Executors提供的静态方法创建不同类型的线程池,比如newFixedThreadPool固定大小线程池、newCachedThreadPool缓存线程池等。 2. 向线程池提交任务:使用submit方法向线程池提交需要执行的任务,也可以使用execute方法提交任务。任务可以是实现了Runnable接口或Callable接口的类,也可以是Lambda表达式。 3. 线程池执行任务:线程池会根据线程池的规模和任务的数量来调度和执行任务,多个任务会并发执行。 4. 关闭线程池:当不再需要线程池时,可以调用线程池的shutdown方法来关闭线程池,确保所有的任务都被执行完毕。 使用线程池的好处有: 1. 提高性能:线程池可以重用线程,避免线程频繁创建和销毁的开销,提高程序的性能。 2. 提供线程管理和调度:线程池可以管理和调度线程,根据线程池的规模和任务的数量来调度和执行任务。 3. 控制资源的使用:线程池可以限制并发线程的数量,避免过度占用系统资源。 在Java开发中,使用线程池是一种推荐的多线程编程方式,也是阿里巴巴在其《Java开发手册》中强制规定的做法。 Java线程的创建是依赖于系统内核的,通过JVM调用系统库创建内核线程,内核线程与Java Thread是1:1的映射关系。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Java线程(六):线程池详解](https://blog.csdn.net/m0_59140023/article/details/124436385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [java线程和线程池](https://blog.csdn.net/qq_29996285/article/details/118955325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值