java典型的池化思想有三种:线程池,字符串常量池,数据库连接池
不使用线程池之前是这样的:
手动创建线程对象---》执行任务----》执行完毕,释放线程对象
有木有发现,这个线程对象用一次就给扔了,想想看你手机打一次电话就扔了,太不合理了
线程池原理:圆圈代表任务,小方框代表线程对象。
那么问题来了:上图有3个线程,5个任务。排队的2个,现在已经是最大量了。再来一个任务呢?答案是在池子里再创建一个线程就可以了。
假设我们池子里最多容纳5个线程对象,如下图,现在池子里和队列里都满了,那再来任务我们就拒绝了。让他去别家
看一下代码里专业参数是怎么形容的
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
}
corePoolSize(线程池目前能使用的线程个数,最大使用的个数是maximumPoolSize)
maximumPoolSize(线程池最大容量):线程池所允许存在的最大线程个数
keepAliveTime(线程存活保持时间),过了并发的高峰期,有的线程可能闲下来了。只要某个线程在一定时间内不被访问,那么就在线程池里销毁它
workQueue(任务队列):就是最多允许几个任务排队。
handler(线程饱和策略):就是我们上面说的“拒绝”,换下一家吧。这个线程池已经扛不住了
数据库连接池
没有数据库连接池的话,每次访问数据库都会建立一个新的连接对象,并且用完会释放掉,这样是很耗费系统资源的。
想想我们是怎么让我们的代码和数据库建立连接的呢?
答:javax.sql.DataSource接口,它负责建立与数据库的连接
创建一个c3po池子:
//C3PO连接池
ComboPooledDataSource DataSource=new ComboPooledDataSource();
//这个ComboPooledDataSource 类间接实现了DataSource接口
接下来固定写法,简单配置一下数据库驱动,用户名,密码和要连接的数据库
准备工作都做好了,现在要去我们创建的池子里拿连接对象了,这里之所以要输出看一下是因为这个Connection其实并不是你想的那个,它其实是c3po自己写的一个代理类NewProxyConnection。
看这个类发现,它实现了Connection接口。并且真正的 Connection也在这个类里被定义成了受保护的属性。那怎么拿到呢(这里想要拿到就是想看看它是不是我们真正的那个Connection,没别的意思,顺便复习一下反射)
这块可以跳过不看,(脑瓜子疼)
写一个方法 通过反射获取 protected Connection inner;
调用上图方法,
Connection connection=dataSource.getConnection();
Object inner=getInner(connection);
System.out.println(inner.getClass().getName())
当然此时你会报错,哈哈,因为你需要暴力反射,注意写的位置
field.setAccessible(true);
filed.get(connetion)
结果就是这样子,控制台打出了我们想要的Connection
走远了,回归正题,我们想要数据库连接池只有一个对象,怎么控制呢,
答:分别设置初始化是几个对象和池子最大可以有几个对象。
此时来了俩个数据库访问(小矩形),第一个拿走了连接对象,但是没有调用.close()方法。线程就会处于等待
字符串常量池
先得搞清楚他在哪,没错,就是在方法区内。那为什么要有这个东东呢???
看下图啊,因为在你定义str1的时候,"hello"是第一次被创建,它就会放到常量池里。当你第二次创建str2也是"hello"的时候,他就不会再在常量池创建一个"hello"了,直接指向创建好的那个。不用白不用嘛
这用脚指头想都是true吧,他俩都指向同一块内存。
String str1="hello";
String str2="hello";
System.out.println(str1==str2);//true
那要是new String("hello")呢?
先来看一个 Person per =new Person()在内存里是咋样展示的。
看的出来,栈里放的是引用类型,所引用的真正内容放在堆里面。
最后难为一下自己??有时间我运行一下看看结果