Wenn ein neues Objekt benötigt wird, wird eines aus dem Pool ausgeliehen. Der Objektpool markiert dann, dass das aktuelle Objekt verwendet wird, und gibt es nach der Verwendung an den Objektpool zurück, damit es ausgeliehen werden kann wieder.
Häufige Nutzungsszenarien des Objektpoolings:
Abfrage 1: Datenbankverbindung herstellen -> Abfrage einleiten -> Verbindung schließen 🎜🎜#Abfrage 2: Datenbankverbindung herstellen -> Eine Abfrage initiieren -> Verbindung schließen
3. Implementierung des Objektpools
Zerstörung
wird oben nicht angezeigt. In einigen Szenarien ist auch der Objektzerstörungsprozess erforderlich, beispielsweise das Lösen einer Verbindung.Initialisierung, Verleihung, Rückgabe, Zerstörung
und andere Vorgänge des darin enthaltenen Objekts zu implementieren.package com.wdbyet.tool.objectpool.mypool; import java.io.Closeable; import java.io.IOException; import java.util.HashSet; import java.util.Stack; /** * @author https://www.wdbyte.com */ public class MyObjectPool<T extends Closeable> { // 池子大小 private Integer size = 5; // 对象池栈。后进先出 private Stack<T> stackPool = new Stack<>(); // 借出的对象的 hashCode 集合 private HashSet<Integer> borrowHashCodeSet = new HashSet<>(); /** * 增加一个对象 * * @param t */ public synchronized void addObj(T t) { if ((stackPool.size() + borrowHashCodeSet.size()) == size) { throw new RuntimeException("池中对象已经达到最大值"); } stackPool.add(t); System.out.println("添加了对象:" + t.hashCode()); } /** * 借出一个对象 * * @return */ public synchronized T borrowObj() { if (stackPool.isEmpty()) { System.out.println("没有可以被借出的对象"); return null; } T pop = stackPool.pop(); borrowHashCodeSet.add(pop.hashCode()); System.out.println("借出了对象:" + pop.hashCode()); return pop; } /** * 归还一个对象 * * @param t */ public synchronized void returnObj(T t) { if (borrowHashCodeSet.contains(t.hashCode())) { stackPool.add(t); borrowHashCodeSet.remove(t.hashCode()); System.out.println("归还了对象:" + t.hashCode()); return; } throw new RuntimeException("只能归还从池中借出的对象"); } /** * 销毁池中对象 */ public synchronized void destory() { if (!borrowHashCodeSet.isEmpty()) { throw new RuntimeException("尚有未归还的对象,不能关闭所有对象"); } while (!stackPool.isEmpty()) { T pop = stackPool.pop(); try { pop.close(); } catch (IOException e) { throw new RuntimeException(e); } } System.out.println("已经销毁了所有对象"); } }
Tatsächlich verfügt Jedis bereits über das entsprechende Jedis-Pool-Verwaltungsobjekt JedisPool. Um die Implementierung des Objektpools hier zu demonstrieren, werden wir jedoch nicht den offiziell bereitgestellten JedisPool verwenden. Das Starten eines Redis-Dienstes wird hier nicht vorgestellt. Unter der Annahme, dass Sie bereits über einen Redis-Dienst verfügen, werden im Folgenden die Maven-Abhängigkeiten vorgestellt, die für die Verbindung mit Redis in Java erforderlich sind.
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.2.0</version> </dependency>
Jedis jedis = new Jedis("localhost", 6379); String name = jedis.get("name"); System.out.println(name); jedis.close();
Es gibt keine Objekte, die ausgeliehen werden können
#🎜🎜 #Wenn Sie verwenden Wenn JMH die Leistung der Redis-Abfrage mithilfe von Objektpooling mit der Methode vergleicht, bei der normalerweise eine Redis-Verbindung erstellt und dann abgefragt wird, um die Verbindung zu schließen, werden Sie feststellen, dass die Leistung der beiden sehr unterschiedlich ist. Im Folgenden sind die Testergebnisse aufgeführt. Es kann festgestellt werden, dass die Leistung nach der Verwendung von Objektpooling etwa fünfmal so hoch ist wie die der Nicht-Pooling-Methode.
Benchmark Mode Cnt Score Error Units
MyObjectPoolTest.test thrpt 15 2612.689 ± 358.767 ops/s
MyObjectPoolTest.testPool thrpt 9 12414.228 ± 11669.484 ops/s
上面自己实现的对象池总归有些简陋了,其实开源工具中已经有了非常好用的对象池的实现,如 Apache 的 commons-pool2
工具,很多开源工具中的对象池都是基于此工具实现,下面介绍这个工具的使用方式。
maven 依赖:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.11.1</version> </dependency>
在 commons-pool2
对象池工具中有几个关键的类。
• PooledObjectFactory
类是一个工厂接口,用于实现想要池化对象的创建、验证、销毁等操作。
• GenericObjectPool
类是一个通用的对象池管理类,可以进行对象的借出、归还等操作。
• GenericObjectPoolConfig
类是对象池的配置类,可以进行对象的最大、最小等容量信息进行配置。
下面通过一个具体的示例演示 commons-pool2
工具类的使用,这里依旧选择 Redis 连接对象 Jedis 作为演示。
实现 PooledObjectFactory
工厂类,实现其中的对象创建和销毁方法。
public class MyPooledObjectFactory implements PooledObjectFactory<Jedis> { @Override public void activateObject(PooledObject<Jedis> pooledObject) throws Exception { } @Override public void destroyObject(PooledObject<Jedis> pooledObject) throws Exception { Jedis jedis = pooledObject.getObject(); jedis.close(); System.out.println("释放连接"); } @Override public PooledObject<Jedis> makeObject() throws Exception { return new DefaultPooledObject(new Jedis("localhost", 6379)); } @Override public void passivateObject(PooledObject<Jedis> pooledObject) throws Exception { } @Override public boolean validateObject(PooledObject<Jedis> pooledObject) { return false; } }
继承 GenericObjectPool
类,实现对对象的借出、归还等操作。
public class MyGenericObjectPool extends GenericObjectPool<Jedis> { public MyGenericObjectPool(PooledObjectFactory factory) { super(factory); } public MyGenericObjectPool(PooledObjectFactory factory, GenericObjectPoolConfig config) { super(factory, config); } public MyGenericObjectPool(PooledObjectFactory factory, GenericObjectPoolConfig config, AbandonedConfig abandonedConfig) { super(factory, config, abandonedConfig); } }
可以看到 MyGenericObjectPool
类的构造函数中的入参有 GenericObjectPoolConfig
对象,这是个对象池的配置对象,可以配置对象池的容量大小等信息,这里就不配置了,使用默认配置。
通过 GenericObjectPoolConfig
的源码可以看到默认配置中,对象池的容量是 8 个。
public class GenericObjectPoolConfig<T> extends BaseObjectPoolConfig<T> { /** * The default value for the {@code maxTotal} configuration attribute. * @see GenericObjectPool#getMaxTotal() */ public static final int DEFAULT_MAX_TOTAL = 8; /** * The default value for the {@code maxIdle} configuration attribute. * @see GenericObjectPool#getMaxIdle() */ public static final int DEFAULT_MAX_IDLE = 8;
下面编写一个对象池使用测试类。
public class ApachePool { public static void main(String[] args) throws Exception { MyGenericObjectPool objectMyObjectPool = new MyGenericObjectPool(new MyPooledObjectFactory()); Jedis jedis = objectMyObjectPool.borrowObject(); String name = jedis.get("name"); System.out.println(name); objectMyObjectPool.returnObject(jedis); objectMyObjectPool.close(); } }
输出日志:
redis get:www.wdbyte.com
释放连接
上面已经演示了 commons-pool2
工具中的对象池的使用方式,从上面的例子中可以发现这种对象池中只能存放同一种初始化条件的对象,如果这里的 Redis 我们需要存储一个本地连接和一个远程连接的两种 Jedis 对象,就不能满足了。那么怎么办呢?
其实 commons-pool2
工具已经考虑到了这种情况,通过增加一个 key 值可以在同一个对象池管理中进行区分,代码和上面类似,直接贴出完整的代码实现。
package com.wdbyet.tool.objectpool.apachekeyedpool; import org.apache.commons.pool2.BaseKeyedPooledObjectFactory; import org.apache.commons.pool2.KeyedPooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.AbandonedConfig; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import redis.clients.jedis.Jedis; /** * @author https://www.wdbyte.com * @date 2022/07/07 */ public class ApacheKeyedPool { public static void main(String[] args) throws Exception { String key = "local"; MyGenericKeyedObjectPool objectMyObjectPool = new MyGenericKeyedObjectPool(new MyKeyedPooledObjectFactory()); Jedis jedis = objectMyObjectPool.borrowObject(key); String name = jedis.get("name"); System.out.println("redis get :" + name); objectMyObjectPool.returnObject(key, jedis); } } class MyKeyedPooledObjectFactory extends BaseKeyedPooledObjectFactory<String, Jedis> { @Override public Jedis create(String key) throws Exception { if ("local".equals(key)) { return new Jedis("localhost", 6379); } if ("remote".equals(key)) { return new Jedis("192.168.0.105", 6379); } return null; } @Override public PooledObject<Jedis> wrap(Jedis value) { return new DefaultPooledObject<>(value); } } class MyGenericKeyedObjectPool extends GenericKeyedObjectPool<String, Jedis> { public MyGenericKeyedObjectPool(KeyedPooledObjectFactory<String, Jedis> factory) { super(factory); } public MyGenericKeyedObjectPool(KeyedPooledObjectFactory<String, Jedis> factory, GenericKeyedObjectPoolConfig<Jedis> config) { super(factory, config); } public MyGenericKeyedObjectPool(KeyedPooledObjectFactory<String, Jedis> factory, GenericKeyedObjectPoolConfig<Jedis> config, AbandonedConfig abandonedConfig) { super(factory, config, abandonedConfig); } }
输出日志:
redis get :www.wdbyte.com
这篇文章中的演示都使用了 Jedis 连接对象,其实在 Jedis SDK 中已经实现了相应的对象池,也就是我们常用的 JedisPool 类。那么这里的 JedisPool 是怎么实现的呢?我们先看一下 JedisPool 的使用方式。
package com.wdbyet.tool.objectpool; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; /** * @author https://www.wdbyte.com */ public class JedisPoolTest { public static void main(String[] args) { JedisPool jedisPool = new JedisPool("localhost", 6379); // 从对象池中借一个对象 Jedis jedis = jedisPool.getResource(); String name = jedis.get("name"); System.out.println("redis get :" + name); jedis.close(); // 彻底退出前,关闭 Redis 连接池 jedisPool.close(); } }
代码中添加了注释,可以看到通过 jedisPool.getResource()
拿到了一个对象,这里和上面 commons-pool2
工具中的 borrowObject
十分相似,继续追踪它的代码实现可以看到下面的代码。
// redis.clients.jedis.JedisPool // public class JedisPool extends Pool<Jedis> { public Jedis getResource() { Jedis jedis = (Jedis)super.getResource(); jedis.setDataSource(this); return jedis; } // 继续追踪 super.getResource() // redis.clients.jedis.util.Pool public T getResource() { try { return super.borrowObject(); } catch (JedisException var2) { throw var2; } catch (Exception var3) { throw new JedisException("Could not get a resource from the pool", var3); } }
竟然看到了 super.borrowObject()
,多么熟悉的方法,继续分析代码可以发现 Jedis 对象池也是使用了 commons-pool2
工具作为实现。既然如此,那么 jedis.close()
方法的逻辑我们应该也可以猜到了,应该有一个归还的操作,查看代码发现果然如此。
// redis.clients.jedis.JedisPool // public class JedisPool extends Pool<Jedis> { public void close() { if (this.dataSource != null) { Pool<Jedis> pool = this.dataSource; this.dataSource = null; if (this.isBroken()) { pool.returnBrokenResource(this); } else { pool.returnResource(this); } } else { this.connection.close(); } } // 继续追踪 super.getResource() // redis.clients.jedis.util.Pool public void returnResource(T resource) { if (resource != null) { try { super.returnObject(resource); } catch (RuntimeException var3) { throw new JedisException("Could not return the resource to the pool", var3); } } }
通过上面的分析,可见 Jedis 确实使用了 commons-pool2
工具进行对象池的管理,通过分析 JedisPool 类的继承关系图也可以发现。
Das obige ist der detaillierte Inhalt vonSo implementieren Sie einen Objektpool in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!