IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java对象池 -> 正文阅读

[Java知识库]Java对象池

对象池技术

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

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

普通对象池

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

线程池

如果池中存放的是线程对象,这种对象池就可以叫做线程池。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..."));
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-02 13:25:12  更:2022-05-02 13:26:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:56:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码