线程池(实现线程的第四种方式)
线程池(实现线程的第四种方式)
1. 线程是什么
线程池其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作。无需反复创建线程而消耗过多的资源
2.使用线程池的优点
**推荐使用线程池来创建线程 **
**1.降低资源消耗:**通过重复利用已创建的线程降低线程创建和销毁带来的消耗
**2.提高响应速度:**当任务到达时,任务可以不需要等待线程创建就能立即执行
**3.提高线程的可管理性: **使用线程池可以统一进行线程分配,调度和监控。
JDK 1.5 之后内置了线程池
两个接口与两个类
3.两大接口
3.1 ExecutorService
普通线程池
提交任务:
void execute(Runnable )
<T> Futer<T> submit (Callable<T> tack || Runnable);
3.2 ScheduledExcutorService
:定时线程池
ScheduledFuture<?> scheduleAtFixedRate(Runnbale command,long initiableDelay,long period,TimeUnit unit);
4. 线程池的工作原理
4.1 线程池工作步骤
1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
2)线程池判断工作队列是否已满。如果工作没满,则将新提交的任务存储在这个工作队列中。反之进入下一流程。
3)线程池判断池中线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。
4.1.1 类
Executor框架最核心的类是ThreadPoolExecutor
,它是线程池的工厂类,用来生产线程池
ThreadPoolExecutor:
ExecutorService
的子类
ThreadPoolExecutor(int corePoolSize,//基本线程池大小int maximumPoolSize,//最大线程池大小long keepAliveTime,//线程活动保持时间TimeUnit unit,//线程活动保持时间单位BlockingQueue<Runnable> workQueue,//阻塞队列RejectExecutionHandler handler)//饱和策略
实例实现:创建一个线程池
ThreadPoolExecutor threadpoolexecutor = new ThreadPoolExecutor(3,5,2000,TimeUnit.MIlLISECONDS,new LinkBlockingDeque<Runnable>());
4.1.2 参数详解
corePoolSize
: 线程池基本大小
2) maximumPoolSize
: 线程池最大数量:线程池允许创建的最大线程数,若使用无界的任务队列这个参数无意义
3)keepAliveTime
:线程活动保持时间:线程池的工作线程空闲后,保持存活的时间。当任务多,且单个任务耗时短时。可以调大这个参数,提高线程的利用率。
4)TimeUnit
:线程活动保持时间的单位 ,可选单位:天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一秒)、纳秒(NANOSECONDS)
5)RejectedExecutionHandler
:饱和策略 当队列和线程都满了,说明线程处于饱和状态,有以下四种策略:
a:
AbortPoilcy
,直接抛出异常b:
CallerRunPoilcy
:只用调用者所在线程来运行任务。c:
DiscardOldestPoilcy
:丢弃队列中最近的一个任务,并执行当前任务d:
DiscardPoilcy
:不处理,丢弃掉
6)runnableTaskQueue
:任务队列 :用于保存等待执行任务的阻塞队列,一个元素的插入操作,必须要等待同时有一个元素的删除操作,否则插入操作就一直阻塞(反之亦然),简而言之,就是插入和删除操作必须配对执行,常见四个阻塞队列:
1.ArrayBlockingQueue:基于数组的有界阻塞队列
2.LinkedBlockingQueue:基于链表的无界阻塞队列
3.内置线程池
FixedThreadPool
,SingleThreadPool
都采取次队列,synchronousQueue
:一个不存元素的无界阻塞队列4.PriorityBlockingQueue:基于优先级的阻塞队列(优先级队列底层是二叉树);
当阻塞队列为无界的链表时,
FututerTask
:可以保证多线程场景下,任务只会被一个线程执行一次。其他线程不再执行此任务。Future接口中的get()方法会阻塞当前线程,直到取到Callable的返回值。
4.1.3 方法
1)execute():用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功
- submit():用于提交需要返回值额度任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成。重载方法get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完
4.1.4 使用线程池提交任务
(1) 使用 execute()方法
package ThreadPoolTest;import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @Name:创建线程池,并提交任务使用executor()* @Author:ZYJ* @Date:2019-06-12-21:18* @Description:*/
public class ThreadPoolDemo {public static void main(String[] args) {RunnableThread runnableThread = new RunnableThread();//创建线程池ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,2000,TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());for(int i=0;i<5;i++){//使用execute()向线程提交任务threadPoolExecutor.execute(runnableThread);}}
}
//实现类实现Runnable接口,覆写run()接口
class RunnableThread implements Runnable{@Overridepublic void run() {for(int i=0;i<50;i++){System.out.println(Thread.currentThread().getName()+","+i);}}
}
(2) 使用submit()方法
package ThreadPoolDemo;
import javax.security.auth.callback.Callback;
import java.util.concurrent.*;/*** @Name: 创建线程池 并使用submit()向线程提交任务* @Author:ZYJ* @Date:2019-06-17-19:48* @Description:*/
public class ThreadPoolTest {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,2000, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<Runnable>());CallableThread callableThread = new CallableThread();for(int i=0;i<5;i++){//实例化 Future对象接收提交的返回值Future<String> future = threadPoolExecutor.submit(callableThread);try {//调用Future类的get方法得到返回值String str = future.get();System.out.println(str);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}
}
//实现类实现CallableThread接口 覆写call类
class CallableThread implements Callable<String> {@Overridepublic String call() throws Exception {for(int i=0;i<50;i++){System.out.println(Thread.currentThread().getName()+","+i);}return Thread.currentThread().getName()+"任务执行完毕!";}
}
4.2 关闭线程池
4.2.1 方法 :
1)shutdown():首先将线程池的状态设置为STOP,然后尝试停止所有正在执行或暂停任务的线程,并返回等待执行任务的列表
2)shutdownNow();只是将状态设置为SHUTDOWN状态,然后中断所有没有正在执行的任务的线程
threadPoolExecutor.shutdown();
4.2.2 原理
遍历线程池中的工作线程,逐个调用interrupt()方法来中断线程,所以无法响应中断的任务永远无法停止。
线程池中的线程被包装为Worker工作线程,具备可重复执行任务的能力
4.3 :合理配置线程池
配置核心池以及最大线程池数量:System.out.println(Runtime.getRuntime().availableProcessors());查看处理器数量。
CPU密集型任务(大数运算,) :NCPU+1;
I/O密集型任务:2*NCPU;
5. Executor框架
在java中使用线程来异步执行任务,java线程的创建与销毁需要一定的开销,如果我们为每一个任务来创建一个新的线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时为每一个任务创建一个新新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。
Java的线程既是工作单元,也是执行机制。从JDK1.5开始,把工作单元与执行机制分离开。工作单元包括Runnable和 Callable,而执行机制由Executor框架提供
5.1 Executor框架的两级调度模型
java线程被一对一映射为本地操作系统线程。java线程启动时,会创建一个本地操作系统线程。当该java线程终止时,这个操作系统线程会被回收。操作系统会调度所有线程并将他们分配给可用的CPU。
在上层,java多线程程序通常应用分为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;
在底层,操作系统内核将这些线程映射到硬件处理器上。
5.1 .四大内置线程池
Executors-线程池的工具(实现)类 他的核心类是ThreadPoolExecutor
,通过工具类Executors可以创建以下类型的线程池
5.1.1固定大小线程池
运用场景:实用于负载较重的服务器(配置较低),来满足资源分配的要求。可重用
**注意:**线程池若不关闭,会一直开启。使用完的线程会自动把线程归还给线程池,可以继续使用
public static ExecutorService newFixedThreadPool(int nThreads)//静态方法,通过类名调用
该方法的返回接口的实现类对象,使用ExecutorService接口接收
核心线程池大小与最大线程池大小相同,不允许有空闲线程出现,
public static ExecutorService newFixedThreadThreadPool(int nThread){return new ThreadPoolExecutor(n Thread,nThread,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());//使用无界队列
}
使用无界队列的影响
1)线程池中的线程数量不超过
corePoolSize
(基本线程池大小)
- 这时最大线程池是一个无效参数
3)线程保持活动时间(keepAliveTime)是一个无效参数
4)运行中的FixedThreadThreadPool(未执行方法shutdown()或shutdownNow()不会拒绝任务)
使用步骤:
1,使用线程池的实现类Executors里提供的静态方法newFixedThreadPool()
生产一个特定线程数量的线程池。
2,创建一个类实现Runnable接口,覆写run()方法,设置线程任务。
3,调用ExecutorService
中的方法submit()方法传递线程任务,开启线程、执行main()方法
4,调用Executor中的方法shutdown()销毁线程池,不建议使用
实例 : FixedThreadPool
的使用
package FixedThreadPoolTest;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @Name: FixedThreadPool的使用* @Author:ZYJ* @Date:2019-06-18-19:38* @Description:*/
public class FixedThreadPoolDemo {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);RunnableImpl runnable = new RunnableImpl();for(int i=0;i<5;i++){executorService.submit(runnable);}executorService.shutdown();}
}
class RunnableImpl implements Runnable{@Overridepublic void run() {for(int j=0;j<10;j++){System.out.println(Thread.currentThread().getName()+"执行任务");}}
}
5.1.2 单线程池-只有一个线程
运用场景:当多线程场景下,需要让人物串行执行时采用
public static ExecutorService newSingleThreadExecutor()
5.1.3 缓存线程池 (无大小限制的线程池):
应用场景:适用于负载较轻的服务器,或执行很多短期的异步任务。
public static ExecutorService newCachedThreadPool()
当任务提交速度大于线程执行速度,会不断创建新线程,有可能无限创建线程将内存写满。
当线程的执行速度大于任务提交速度,只会创建若干个有限线程。
5.1.4 定时调度池
public static ExecutorService newScheduledThreadPool()
executorService.schedule(Runnable command,long delay,TimeUnit unit);
延迟delay个时间单元后创建nThreads个线程执行command任务。
scheduleAtFixedRate(Runnbale command,long initialDelay,long period,TimeUnit unit)s
延迟delay个单元后每隔period单元时间就执行一次command任务
5.2 扩展问题
JDK1.7 任务分工Fork/Join 单机版的MapReduce
JDK1.8 StampedLock
性能优于ReentrantReadWriteLock
JUC:并发工具类
CountDownLatch
闭锁
CyclicBarrier
循环栅栏
Semophore
信号量Exchanger 交换器
线程池(实现线程的第四种方式)相关推荐
- ThreadPoolExecutor线程池,shutdown和shutdownNow关闭线程池方式对比,以及确保线程池能够彻底关闭的一种方式
1. ThreadPoolExecutor线程池 1.1 创建线程池,构造方法的几个参数说明及创建如下. 1.2 shutdown方式关闭线程池 a. 空闲且能interrupt表示该线程处于阻塞等待 ...
- python单例模式数据库连接池_Python实现单例模式的四种方式
# 单例模式实现方式一:类方法import settingsclass MySQL:__instance=Nonedef __init__(self, ip, port):self.ip = ips ...
- java并发编程基础系列(五): 创建线程的四种方式
线程的创建一共有四种方式: 继承于Thread类,重写run()方法: 实现Runable接口,实现里面的run()方法: 使用 FutureTask 实现有返回结果的线程 使用ExecutorSer ...
- java中创建线程的四种方式及线程池详解
众所周知,我们在创建线程时有四种方法可以用,分别是: 1.继承Thread类创建线程 2.实现Runnable接口创建线程 3.使用Callable和Future创建线程 4.使用线程池创建(使用ja ...
- 创建线程的四种方式(Thread、Runnable、线程池、Callable)
目录 一.直接继承Thread类,然后重写run方法 二.实现Runnable接口 三.线程池创建 四.实现Callable接口 创建线程有四种方式:1.继承Thread类 2.实现Runnabl ...
- iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(上)
2017-07-08 remember17 Cocoa开发者社区 目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 ...
- java实现线程的方式_java多线程实现的四种方式
java多线程实现的四种方式1.继承Thread类,重写run方法(其实Thread类本身也实现了Runnable接口) 2.实现Runnable接口,重写run方法 3.实现Callable接口,重 ...
- Java 并发 多线程:创建线程的四种方式
Java 并发 多线程: 创建线程的四种方式 继承 Thread 类并重写 run 方法 实现 Runnable 接口 实现 Callable 接口 使用线程池的方式创建 1. 通过继承 Thread ...
- C#开启线程的四种方式
C#开启线程的四种方式 转载于:https://www.cnblogs.com/macT/p/9916425.html
最新文章
- PicGo 配置Gitee 图床
- 手撸一个npm包,安利一下duiba-sprite
- mysql猎豹_猎豹网校MySQL数据库
- 10分钟教你看懂mongodb的npm包
- Linux驱动调试中的Debugfs的使用简介
- 对CSS了解-overflow:hidden
- 【无码专区7】括号序列(思维)
- python机器人算法_DBscan算法及其Python实现
- 【HRBUST - 1996】数学等式 (HASH 或 二分)
- mac php errorlog,Mac下使用php的error_log()函数发送邮件
- html5 在新标签页打开,Chrome,Javascript,window。在新标签页中打开
- 漫画 | 为什么 MySQL 数据库要用 B+ 树存储索引?
- 【长文详解】T5: Text-to-Text Transfer Transformer 阅读笔记
- 站立会议07(第二次冲刺)
- 搭建FileZilla
- 一款好看的pycharm主题Atom One Dark
- 基于AD9833的三角波及正弦波发生器资料(含51程序)
- Quartus prime工程中各种文件的后缀及意义
- win7电脑怎么提升开机速度
- python:defaultdict 对象
热门文章
- 全网疯传:程序员的酒后真言
- 谈随机数的生成函数(简称随机函数)的算法与真正的运用
- 怎样解决 -- 电脑点击右键反应慢 ?
- 电脑卡顿反应慢怎么处理?提升反应速度的方法
- 让AI来告诉你什么叫幽灵堵车
- 图像增广:强化深度学习的视觉表现力
- 服务器系统如何校验md5值,怎么验证md5-NTP的MD5加密
- html table nowrap,HtmlTableCell.NoWrap 属性 (System.Web.UI.HtmlControls) | Microsoft Docs
- 【Docker】将本地镜像推送到远程库/私有库
- 将本地的Git仓库推送到远程仓库的两种方式