单例模式在项目实战中的几个应用
Posted kuangdw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式在项目实战中的几个应用相关的知识,希望对你有一定的参考价值。
一、单例模式简单理解
单例模式:即某个类在程序运行过程中只被实例化一次,也就是说该类在程序的生存周期里只有一个实例对象。
使用单例模式好处:由于这个类只实例化一次,不管多少个类中用到了这个类,也都只有一个该类的对象。因此,
减少了类实例对象的创建-->减小了GC压力-->提升了程序的性能。
二、单例模式的几种常见写法
/**
* 饿汉式(线程安全)。类加载时就创建唯一的单例实例,不管后面用不用都创建了再说
* 空间换时间的思想
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
/**
* 懒汉式(非线程安全,可以在创建函数前加synchronized关键字变为线程安全)
* 单例实例在使用时才创建
*/
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){ //方法前加synchronized关键字变为线程安全,但是会增加创建的时间消耗
if (instance==null){
instance = new Singleton();
}
return instance;
}
}
/**
* 懒汉方式(线程安全双重检查锁版本)
*/
public class Singleton{
private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton() {
if (singleton==null){ //第一重检查
synchronized (Singleton.class){
if (singleton==null){ //第二重检查
singleton = new Singleton();
}
}
}
return singleton;
}
三、单例模式在Redis工具类中的使用
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Tuple;
/**
* Redis连接池配置及使用方法
*/
public class Redis {
private static final Logger logger = LoggerFactory.getLogger(Redis.class);
private static ReentrantLock lock = new ReentrantLock();
private static Redis instance;
private JedisPool pool = null;
public Redis(){
}
//线程安全的单例模式
public static Redis getInstance(){
if (instance==null){
lock.lock();
if (instance==null){
instance = new Redis();
}
lock.unlock();
}
return instance;
}
public void initialRedisPool() {
//Redis服务器IP
String ADDR = "localhost";
//Redis的端口号
int PORT = 6379;
//可用连接实例的最大数目,默认值为8;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted,再获取jedis就会报错了。
//这里我们设置2000就足够了
int MAX_ACTIVE = 2000;
//一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
int MAX_IDLE = 200;
//等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
int MAX_WAIT = 10000;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
boolean TEST_ON_BORROW = true;
/**
* 初始化Redis连接池
*/
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
pool = new JedisPool(config, ADDR, PORT);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取Jedis对象
*
* @return Jedis
*/
public synchronized Jedis getJedis() {
Jedis jedis = null;
if (pool == null){
initialRedisPool();
}
jedis = pool.getResource();
return jedis;
}
//下面就是一些其他的对应于redis命令的工具方法了
//比如set(...),get(...),lpush(...),hset(...)等
使用起来就很简单了,比如:
String value = Redis.getInstance().get(String key)
//或者是
Redis redisObj = Redis.getInstance()
String value = redisObj.get(String key)
四、单例模式在线程池创建中的使用
我在项目中碰到一个这样的场景:
1)某个接口的并发请求较大;
2)对收到的数据要进行复杂的验证及数据库相关操作;
3)响应速度不能太慢,至少得2秒内吧;
于是正好可以拿线程池来练练手,下面分享一下我的练手代码(项目实战中根据需求稍作修改即可应用):
本人实际项目中亲测,并且使用JMeter做了压测,吞吐量大,响应速度巨快!
1 任务类(这是一个实现Callable的线程任务,因为我需要返回结果)
package service;
import java.util.concurrent.Callable;
/**
* 任务类
*/
public class MyTask implements Callable {
//假设我们需要处理传入进来的数据
private final String data;
public MyTask(final String data){
this.data = data;
}
@Override
public Object call() throws Exception {
System.out.println("==============正在处理收到的data:" + data);
Thread.sleep(1000); //模拟处理数据需要花点小时间
return "处理成功";
}
}
2 处理任务的线程工具类
package service;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
public class TaskUtil {
private static ThreadPoolExecutor poolExecutor = ThreadPoolConfig.getInstance();
public static String submit(Callable callable) throws ExecutionException, InterruptedException {
String result = "";
//使用submit()方法提交任务,execute()方法不接受Callable线程任务
Future<String> future = poolExecutor.submit(callable);
//获取结果
result = future.get();
return result;
}
}
3 线程池创建类
package service;
public class ThreadPoolConfig {
//核心线程数
private static final int corePoolSize = 32;
//最大线程数
private static final int maxPoolSize = 48;
//线程最大空闲时间
private static final int keepAlive = 30;
//线程池缓冲队列
private static final BlockingQueue poolQueue = new LinkedBlockingQueue(64);
private static ThreadPoolExecutor poolExecutor;
private ThreadPoolConfig(){
}
/**
* 单例模式获取
* @return
*/
public static ThreadPoolExecutor getInstance(){
if (poolExecutor == null){
//使用synchronized保证多线程情况下也是单例的
synchronized (ThreadPoolConfig.class){
if (poolExecutor == null){
poolExecutor = new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAlive,TimeUnit.SECONDS,poolQueue,new
ThreadPoolExecutor.DiscardOldestPolicy());
}
}
}
return poolExecutor;
}
}
//这里给使用ThreadPoolExecutor创建线程池的重要参数解释(来自源码的一部分)
//百度上搜一下也一大把的解释,但是也基本都是来自于源码(建议狠一点,多看源码中的注释和代码)
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
/*public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}*/
4 测试类
package service;
import java.util.concurrent.ExecutionException;
public class TestThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
for (int i = 0; i < 50; i++) {
System.out.println("-------收到请求任务" + i+"--------");
//模拟从请求中拿到的数据
String requestData = "this is request data to deal with"+i;
//将数据处理任务丢给线程池异步处理
String re = ThreadService.submit(new MyTask(requestData));
//打印返回的结果(实际项目中将结果封装一下返回给前端就行了)
System.out.println("返回结果="+re);
}
}
}
以上是关于单例模式在项目实战中的几个应用的主要内容,如果未能解决你的问题,请参考以下文章