关于对象池,形象地说就是事先创建好了一些某类型的对象放在对象池中。当程序(线程)需要使用这种对象的时候,直接从对象池中获取该对象。
然而也有很多问题需要注意,一些不容忽视的问题就是:
1.原子操作的问题,共享资源区是不能同时访问的,所以使用synchronized来并发防止访问错误。
2.线程阻塞问题,当对象池中对象全部在使用中,已经没有空闲对象,然而此时又有一个线程向对象池申请对象,那么该线程将会陷入阻塞状态,所谓阻塞就是让线程进入有限等待中,不断的检查对象池中是否有空闲对象,若此时有对象在使用完之后被释放,那么该等待程序将从等待中跳出,成功获取空闲对象。
用Java语言实现方法:
1.首先创建一个对象池ObjectPool类:
package com.work_09;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.Vector;
class ObjectPool<T> {
private int numObjects = 3; // 对象池的大小
private int maxObjects = 6; // 对象池最大的大小
private Vector objects = null;
private Class<?> clazz = null;
public ObjectPool(Class cla){
clazz = cla;
}
/*** 创建一个对象池
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InstantiationException ***/
@SuppressWarnings("unchecked")
public synchronized void createPool() throws Exception{
Constructor<?> con = clazz.getDeclaredConstructor();
// 确保对象池没有创建。如果创建了,保存对象的向量 objects 不会为空
if (objects != null) {
return; // 如果己经创建,则返回
}
// 创建保存对象的向量 , 初始时有 0 个元素
objects = new Vector();
//根据 numObjects 中设置的值,循环创建指定数目的对象
for(int i=0;i<this.numObjects;i++)
{
System.out.println("P创建了一个对象");
T obj = (T) con.newInstance();
objects.addElement(new PooledObject(obj));
}
}
public synchronized void createObjects() throws Exception, SecurityException
{
Constructor<?> con = clazz.getDeclaredConstructor();
for(int i=0;i<this.numObjects;i++)
{
if(this.objects.size() < maxObjects)
{
System.out.println("C创建了一个对象");
T obj = (T) con.newInstance();
objects.addElement(new PooledObject(obj));
}
}
}
public synchronized T getObject() throws Exception
{
// 确保对象池己被创建
if (objects == null) {
return null; // 对象池还没创建,则返回 null
}
T conn = getFreeObject(); // 获得一个可用的对象
// 如果目前没有可以使用的对象,即所有的对象都在使用中
while (conn == null) {
wait(250);
conn = getFreeObject(); // 重新再试,直到获得可用的对象,如果
// getFreeObject() 返回的为 null,则表明创建一批对象后也不可获得可用对象
}
System.out.println("成功 获取 对象!!!");
return conn;// 返回获得的可用的对象
}
private T getFreeObject() throws Exception{
T obj = findFreeObject();
if (obj == null) {
createObjects(); //如果目前对象池中没有可用的对象,创建一些对象
// 重新从池中查找是否有可用对象
obj = findFreeObject();
// 如果创建对象后仍获得不到可用的对象,则返回 null
if (obj == null) {
return null;
}
}
return obj;
}
private T findFreeObject()
{
T obj = null;
PooledObject<T> pObj = null;
// 获得对象池向量中所有的对象
Enumeration enumerate = objects.elements();
// 遍历所有的对象,看是否有可用的对象
while (enumerate.hasMoreElements())
{
pObj = (PooledObject) enumerate.nextElement();
// 如果此对象不忙,则获得它的对象并把它设为忙
if (!pObj.isBusy())
{
obj = pObj.getObject();
pObj.setBusy(true);
return obj;
}
}
return obj;// 返回找到到的可用对象
}
public void returnObject(T obj) {
// 确保对象池存在,如果对象没有创建(不存在),直接返回
if (objects == null) {
return;
}
PooledObject<T> pObj = null;
Enumeration enumerate = objects.elements();
// 遍历对象池中的所有对象,找到这个要返回的对象对象
while (enumerate.hasMoreElements()) {
pObj = (PooledObject) enumerate.nextElement();
// 先找到对象池中的要返回的对象对象
if (obj == pObj.getObject()) {
// 找到了 , 设置此对象为空闲状态
System.out.println("成功 释放 对象~~~");
pObj.setBusy(false);
break;
}
}
}
public synchronized void closeObjectPool() {
// 确保对象池存在,如果不存在,返回
if (objects == null) {
return;
}
PooledObject<T> pObj = null;
Enumeration enumerate = objects.elements();
while (enumerate.hasMoreElements()) {
pObj = (PooledObject) enumerate.nextElement();
// 如果忙,等 5 秒
if (pObj.isBusy()) {
wait(5000); // 等 5 秒
}
// 从对象池向量中删除它
objects.removeElement(pObj);
}
// 置对象池为空
objects = null;
}
private void wait(int mSeconds) {
try {
Thread.sleep(mSeconds);
}
catch (InterruptedException e) {
}
}
}
2.这里还有一个PooledObject类,它用来封装对象池中的对象,主要添加了一个busy参数,来标志对象的使用状态:
package com.work_09;
class PooledObject<T>{
T objection = null;// 对象
boolean busy = false; // 对象是否正在使用的标志,默认没有正在使用
// 构造函数,根据一个 Object 构告一个 PooledObject 对象
public PooledObject(T objection) {
this.objection = objection;
}
// 返回此对象中的对象
public T getObject() {
return objection;
}
// 设置此对象的,对象
public void setObject(T objection) {
this.objection = objection;
}
// 获得对象对象是否忙
public boolean isBusy() {
return busy;
}
// 设置对象的对象正在忙
public void setBusy(boolean busy) {
this.busy = busy;
}
}
3.测试程序Pool_Test:
package com.work_09;
import com.work_06.Student;
public class Pool_Test implements Runnable {
static ObjectPool<Student> objPool = null;
public static void main(String[] args) throws Exception {
objPool = new ObjectPool<Student>(Student.class); //创建一个静态池
objPool.createPool(); //创建池,生成最小数目的对象
for(int i=0;i<10;i++){ //十个线程并发访问
Test();
}
}
public static void Test() throws Exception
{
Thread t0 = new Thread(new Pool_Test());
t0.start();
}
public Pool_Test() throws Exception {
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
Student s = null;
try {
s = objPool.getObject(); //获取对象
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(3000); //为了看到阻塞效果,让线程延迟3秒。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
objPool.returnObject(s); //释放对象
}
}
4.运行结果:
P创建了一个对象
P创建了一个对象
P创建了一个对象
成功 获取 对象!!!
成功 获取 对象!!!
成功 获取 对象!!!
C创建了一个对象
C创建了一个对象
C创建了一个对象
成功 获取 对象!!!
成功 获取 对象!!!
成功 获取 对象!!!
成功 释放 对象~~~
成功 释放 对象~~~
成功 释放 对象~~~
成功 释放 对象~~~
成功 释放 对象~~~
成功 释放 对象~~~
成功 获取 对象!!!
成功 获取 对象!!!
成功 获取 对象!!!
成功 获取 对象!!!
成功 释放 对象~~~
成功 释放 对象~~~
成功 释放 对象~~~
成功 释放 对象~~~
结果分析:因为对象池起初大小为3,在前三个对象获取到对象后并未释放,第四个线程申请对象时,已没有空闲对象,所以此时在对象池中又创建了一些对象(3个),但是对象数十不能超过最大大小:6的。当第七个以后的线程来申请对象时,就会陷入阻塞状态,(一直循环等待空闲资源),直到前面的线程将资源(对象)释放后,才能获取资源,跳出循环。
以上纯属博主个人观点,也有很多不足和片面的看法,敬请见谅,也欢迎有同样爱好的小伙伴来与我讨论!