Java对象池

Java对象池,第1张

文章目录
    • 对象池技术
      • 普通对象池
      • 线程池
      • 数据库连接池
    • 对象池的实现思路
      • 热身
      • 对象池优化

对象池技术

如果创建/获取某些对象花费的代价很高,则需要考虑——是否可以将获取到的对象组织起来重复利用,避免频繁地创建和释放这类对象。

简言之,将得到的对象组织起来重复使用的技术就是对象池技术。

普通对象池

如果池中存放的是普通对象,就是一般意义上的普通对象池。

线程池

如果池中存放的是线程对象,这种对象池就可以叫做线程池。Java中创建线程,需要保存用户态的上下文,切换到内核态向OS申请创建,代价很高。

数据库连接池

如果池中存放的是数据库连接,这种对象池就可以叫做数据库连接池。客户端跟数据库服务器建立起网络连接的过程,会涉及到多个来回的网络交互,代价很高。

对象池的实现思路

普通对象池是最简单的一种,其实现思路分两步:

  1. 程序初始化时,预先创建一定数量的对象,放到一个集合容器(比如ArrayList)中,以备后续使用。
  2. 需要在业务逻辑中用到这类对象时,先从容器中取一个出来,使用完毕再还回去(重新放入容器)。

我们以计算MD5/SHA-256的实现为例,先来写一写普通对象池的实现。

热身

说到MD5,你肯定见过类似的代码:

//MD5Utils.java
public final static String md5AsHex(byte[] input) {
    try {                    
        MessageDigest mdInst = MessageDigest.getInstance("MD5");            
        mdInst.update(input);            
        byte[] md = mdInst.digest();
        //encodeHex将字节数组转成16进制的字符串表示
        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);
}
//本机测试,timeTaken耗时大致是14ms~22ms

计算一次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";
    /**
     * 预先生成{@code initialSize}数量的MessageDigest对象
     */
	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;
    // 使用的算法是MD5还是SHA-256
	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..."));
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/795790.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存