线程池(实现线程的第四种方式)

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 参数详解

  1. 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.内置线程池FixedThreadPoolSingleThreadPool都采取次队列,synchronousQueue:一个不存元素的无界阻塞队列

4.PriorityBlockingQueue:基于优先级的阻塞队列(优先级队列底层是二叉树);

当阻塞队列为无界的链表时,FututerTask:可以保证多线程场景下,任务只会被一个线程执行一次。其他线程不再执行此任务。

Future接口中的get()方法会阻塞当前线程,直到取到Callable的返回值。

4.1.3 方法

1)execute():用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功

  1. 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(基本线程池大小)

  1. 这时最大线程池是一个无效参数

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 交换器

线程池(实现线程的第四种方式)相关推荐

  1. ThreadPoolExecutor线程池,shutdown和shutdownNow关闭线程池方式对比,以及确保线程池能够彻底关闭的一种方式

    1. ThreadPoolExecutor线程池 1.1 创建线程池,构造方法的几个参数说明及创建如下. 1.2 shutdown方式关闭线程池 a. 空闲且能interrupt表示该线程处于阻塞等待 ...

  2. python单例模式数据库连接池_Python实现单例模式的四种方式

    # 单例模式实现方式一:类方法import settings​class MySQL:__instance=Nonedef __init__(self, ip, port):self.ip = ips ...

  3. java并发编程基础系列(五): 创建线程的四种方式

    线程的创建一共有四种方式: 继承于Thread类,重写run()方法: 实现Runable接口,实现里面的run()方法: 使用 FutureTask 实现有返回结果的线程 使用ExecutorSer ...

  4. java中创建线程的四种方式及线程池详解

    众所周知,我们在创建线程时有四种方法可以用,分别是: 1.继承Thread类创建线程 2.实现Runnable接口创建线程 3.使用Callable和Future创建线程 4.使用线程池创建(使用ja ...

  5. 创建线程的四种方式(Thread、Runnable、线程池、Callable)

    目录 一.直接继承Thread类,然后重写run方法 二.实现Runnable接口 三.线程池创建 四.实现Callable接口 创建线程有四种方式:1.继承Thread类   2.实现Runnabl ...

  6. iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(上)

    2017-07-08 remember17 Cocoa开发者社区 目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 ...

  7. java实现线程的方式_java多线程实现的四种方式

    java多线程实现的四种方式1.继承Thread类,重写run方法(其实Thread类本身也实现了Runnable接口) 2.实现Runnable接口,重写run方法 3.实现Callable接口,重 ...

  8. Java 并发 多线程:创建线程的四种方式

    Java 并发 多线程: 创建线程的四种方式 继承 Thread 类并重写 run 方法 实现 Runnable 接口 实现 Callable 接口 使用线程池的方式创建 1. 通过继承 Thread ...

  9. C#开启线程的四种方式

    C#开启线程的四种方式 转载于:https://www.cnblogs.com/macT/p/9916425.html

最新文章

  1. PicGo 配置Gitee 图床
  2. 手撸一个npm包,安利一下duiba-sprite
  3. mysql猎豹_猎豹网校MySQL数据库
  4. 10分钟教你看懂mongodb的npm包
  5. Linux驱动调试中的Debugfs的使用简介
  6. 对CSS了解-overflow:hidden
  7. 【无码专区7】括号序列(思维)
  8. python机器人算法_DBscan算法及其Python实现
  9. 【HRBUST - 1996】数学等式 (HASH 或 二分)
  10. mac php errorlog,Mac下使用php的error_log()函数发送邮件
  11. html5 在新标签页打开,Chrome,Javascript,window。在新标签页中打开
  12. 漫画 | 为什么 MySQL 数据库要用 B+ 树存储索引?
  13. 【长文详解】T5: Text-to-Text Transfer Transformer 阅读笔记
  14. 站立会议07(第二次冲刺)
  15. 搭建FileZilla
  16. 一款好看的pycharm主题Atom One Dark
  17. 基于AD9833的三角波及正弦波发生器资料(含51程序)
  18. Quartus prime工程中各种文件的后缀及意义
  19. win7电脑怎么提升开机速度
  20. python:defaultdict 对象

热门文章

  1. 全网疯传:程序员的酒后真言
  2. 谈随机数的生成函数(简称随机函数)的算法与真正的运用
  3. 怎样解决 -- 电脑点击右键反应慢 ?
  4. 电脑卡顿反应慢怎么处理?提升反应速度的方法
  5. 让AI来告诉你什么叫幽灵堵车
  6. 图像增广:强化深度学习的视觉表现力
  7. 服务器系统如何校验md5值,怎么验证md5-NTP的MD5加密
  8. html table nowrap,HtmlTableCell.NoWrap 属性 (System.Web.UI.HtmlControls) | Microsoft Docs
  9. 【Docker】将本地镜像推送到远程库/私有库
  10. 将本地的Git仓库推送到远程仓库的两种方式