线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。
基于以下几个原因在多线程应用程序中使用线程是必须的:
线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。
线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。
线程池根据当前在系统中运行的进程来优化线程时间片。
线程池允许我们开启多个任务而不用为每个线程设置属性。
线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。
线程池可以用来解决处理一个特定请求最大线程数量限制问题。
连接池:
1、连接池是面向数据库连接的
2、连接池是为了优化数据库连接资源
3、连接池有点类似在客户端做优化
数据库连接是一项有限的昂贵资源,一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 线程池的原理类似于操作系统中的缓冲区的概念。
先启动若干数量的线程,并让这些线程都处于睡眠状态,当客户端有一个新请求时,就会唤醒线程池中的某一个睡眠线程,让它来处理客户端的这个请求,当处理完这个请求后,线程又处于睡眠状态。
也许有人质疑:为什么要搞得这么麻烦,如果每当客户端有新的请求时,我就创建一个新的线程不就完了吗?这也许是个不错的方法,因为它能使你的代码相对容易一些,但是却忽略了性能问题。
假如有一个省级数据大集中的银行网络中心,高峰期每秒的客户端请求并发数超过100,如果为每个客户端请求创建一个新线程的话,那耗费的CPU时间和内存将是惊人的,如果采用一个拥有200个线程的线程池,那将会节约大量的系统资源,使得更多的CPU时间和内存用来处理实际的商业应用,而不是频繁的线程创建与销毁。
线程池:
1.、线程池是面向后台程序的
2、线程池是是为了提高内存和CPU效率
3、线程池有点类似于在服务端做优化
线程池是一次性创建一定数量的线程(应该可以配置初始线程数量的),当用请求过来不用去创建新的线程,直接使用已创建的线程,使用后又放回到线程池中。
避免了频繁创建线程,及销毁线程的系统开销,提高是内存和CPU效率。
一个数据库连接池对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库连接对象),由应用程序动态的对池中的连接进行申请,使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。
连接池技术尽可能多的重用了消耗内存的资源,大大节省了内存,提高了服务器的服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来检视数据库连接的数量、使用情况等。
相同点:
都是事先准备好资源,避免频繁创建和销毁的代价。
1.ThreadPoolExecutor构造( 以6个参数的为例子)
* @param corePoolSize 核心池大小 int
* @param maximumPoolSize 最大池大小 int
* @param keepAliveTime 保活时间 long(任务完成后要销毁的延时)
* @param unit 时间单位 决定参数3的单位,枚举类型的时间单位
* @param workQueue 工作队列 用于存储任务的工作队列(BlockingQueue接口类型)
* @param threadFactory 线程工厂 用于创建线程
*
* */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
2.自定义实现案例
package ThreadPool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Create by SunnyDay on 2018/11/27
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
/**手写线程池
* @param corePoolSize 核心池大小 int
* @param maximumPoolSize 最大池大小 int
* @param keepAliveTime 保活时间 long(任务完成后要销毁的延时)
* @param unit 时间单位 决定参数3的单位,枚举类型的时间单位
* @param workQueue 工作队列 用于存储任务的工作队列(BlockingQueue接口类型)
* @param threadFactory 线程工厂 用于创建线程
*
*线程不是越多越好,google工程师推荐 线程个数=cpu核心数+1(例如四核的开5个线程最好)
* */
// 参数任务上限
LinkedBlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(100);
ThreadFactory threadFactory = new ThreadFactory() {
// int i = 0; 用并发安全的包装类
AtomicInteger atomicInteger = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
//创建线程 吧任务传进来
Thread thread = new Thread(r);
// 给线程起个名字
thread.setName("MyThread" + atomicInteger.getAndIncrement());
return thread;
}
};
ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 1, TimeUnit.SECONDS, blockingQueue, threadFactory);
for (int i = 0; i < 100; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
/**
* 编写代码(如下文的函数)同时只允许五个线程并发访问:
*
* 1 若果使用 synchronized的话避免了并发安全问题,但是不满足题目的要求,
* 因为题目要求一次让五个线程访问,使用锁的时候一次只能访问一个。
* 2 解决思路:使用信号量或者使用线程池
*
* 3 自己手动封装线程池(明白启动策略)
*/
private static void method() throws InterruptedException {
System.out.println("ThreadName" + Thread.currentThread().getName() + "进来了");
Thread.sleep(2000);
System.out.println("ThreadName" + Thread.currentThread().getName() + "出去了");
}
}