对象池技术
如果创建/获取某些对象花费的代价很高,则需要考虑——是否可以将获取到的对象组织起来重复利用,避免频繁地创建和释放这类对象。
简言之,将得到的对象组织起来重复使用的技术就是对象池技术。
普通对象池
如果池中存放的是普通对象,就是一般意义上的普通对象池。
线程池
如果池中存放的是线程对象,这种对象池就可以叫做线程池。Java中创建线程,需要保存用户态的上下文,切换到内核态向OS申请创建,代价很高。
数据库连接池
如果池中存放的是数据库连接,这种对象池就可以叫做数据库连接池。客户端跟数据库服务器建立起网络连接的过程,会涉及到多个来回的网络交互,代价很高。
对象池的实现思路
普通对象池是最简单的一种,其实现思路分两步:
- 程序初始化时,预先创建一定数量的对象,放到一个集合容器(比如
ArrayList )中,以备后续使用。 - 需要在业务逻辑中用到这类对象时,先从容器中取一个出来,使用完毕再还回去(重新放入容器)。
我们以计算MD5/SHA-256 的实现为例,先来写一写普通对象池的实现。
热身
说到MD5 ,你肯定见过类似的代码:
public final static String md5AsHex(byte[] input) {
try {
MessageDigest mdInst = MessageDigest.getInstance("MD5");
mdInst.update(input);
byte[] md = mdInst.digest();
return encodeHex(md);
} catch (Exception e) {
e.printStackTrace();
}
}
看起来这个工具类很不错,使用起来也很方便。不过先别急,我们来考察一下性能问题。
public static void main(String[] args) throws Exception {
String input = "Hello MD5";
long begin = System.currentTimeMillis();
String hexStr = md5AsHex(input.getBytes("UTF-8"));
long end = System.currentTimeMillis();
System.out.println("timeTaken: " + (end-begin) + "ms");
System.out.println(hexStr);
}
计算一次MD5 居然耗时14ms~22ms 。所以,上面MD5Utils.md5AsHex() 方法实现有严重的性能问题!
如果更进一步跟踪,你会发现问题出在这一行代码耗时太高:
MessageDigest mdInst = MessageDigest.getInstance("MD5");
也就是说,得到MessageDigest 对象的代价特别高。
对象池优化
我们来封装一个MessageDigestProvider 类,在程序初始化的时候能够预先生成一定数量的MessageDigest("MD5") 或者MessageDigest("SHA-256") 对象。
public class MessageDigestProvider {
public static final String SHA_256 = "SHA-256";
public static final String MD5 = "MD5";
public static MessageDigestProvider newSHA256Instance(int initialSize) {
return new MessageDigestProvider(SHA_256, initialSize);
}
public static MessageDigestProvider newMD5Instance(int initialSize) {
return new MessageDigestProvider(MD5, initialSize);
}
private final List<MessageDigest> freeList;
private final String algorithm;
private MessageDigestProvider(String algorithm, int initialSize) {
this.algorithm = algorithm;
freeList = new ArrayList<>(initialSize);
try {
for (int i = 0; i < initialSize; i++) {
freeList.add(MessageDigest.getInstance(algorithm));
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
需要用到对象的时候从容器中取一个出来,用完之后再放回容器中。
这里用的容器是ArrayList ,拿取和放回的操作都从尾巴上进行,性能最好。
private MessageDigest request() {
MessageDigest md = null;
synchronized (this) {
int size = freeList.size();
if (size > 0) {
size -= 1;
md = freeList.get(size);
freeList.remove(size);
}
}
if (md == null) {
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return md;
}
private synchronized void release(MessageDigest md) {
freeList.add(md);
}
以上,关于对象池技术相关的代码都已经写好了,接下来就是把计算MD5 的相关逻辑封装进去。
完整的,多线程安全的MessageDigest 对象池实现如下:
public class MessageDigestProvider {
public static final String SHA_256 = "SHA-256";
public static final String MD5 = "MD5";
public static final Charset UTF8 = Charset.forName("UTF-8");
private static final char[] HEX_CHARS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f' };
public static MessageDigestProvider newSHA256Instance(int initialSize) {
return new MessageDigestProvider(SHA_256, initialSize);
}
public static MessageDigestProvider newMD5Instance(int initialSize) {
return new MessageDigestProvider(MD5, initialSize);
}
public String digestAsHex(String s) {
byte[] bytes = s.getBytes(UTF8);
MessageDigest md = request();
md.update(bytes);
byte[] b = md.digest();
release(md);
return encodeHex(b);
}
public String digestAsHex(byte[] bytes) {
MessageDigest md = request();
md.update(bytes);
byte[] b = md.digest();
release(md);
return encodeHex(b);
}
private final List<MessageDigest> freeList;
private final String algorithm;
public static String encodeHex(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length << 1);
for (byte b : bytes) {
sb.append(HEX_CHARS[b >>> 4 & 15]);
sb.append(HEX_CHARS[b & 15]);
}
return sb.toString();
}
private MessageDigestProvider(String algorithm, int initialSize) {
this.algorithm = algorithm;
freeList = new ArrayList<>(initialSize);
try {
for (int i = 0; i < initialSize; i++) {
freeList.add(MessageDigest.getInstance(algorithm));
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
private MessageDigestProvider(String algorithm) {
this(algorithm, 1);
}
private MessageDigest request() {
MessageDigest md = null;
synchronized (this) {
int size = freeList.size();
if (size > 0) {
size -= 1;
md = freeList.get(size);
freeList.remove(size);
}
}
if (md == null) {
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return md;
}
private synchronized void release(MessageDigest md) {
freeList.add(md);
}
}
使用起来也是一样很方便:
public static void main(String[] args) throws Exception {
MessageDigestProvider sha256Provider = MessageDigestProvider.newSHA256Instance(5);
System.out.println(sha256Provider.digestAsHex("Test Data String..."));
}
|