单例模式在项目实战中的几个应用
一、单例模式简单理解
单例模式:即某个类在程序运行过程中只被实例化一次,也就是说该类在程序的生存周期里只有一个实例对象。
使用单例模式好处:由于这个类只实例化一次,不管多少个类中用到了这个类,也都只有一个该类的对象。因此,
减少了类实例对象的创建-->减小了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); } } }
相关推荐
fraternityjava 2020-06-14
gongruitao 2020-05-02
xiaoemo0 2020-04-08
付春杰Blog 2020-03-26
Kele0 2020-05-30
elizabethxxy 2020-04-27
JF0 2020-03-20
ahnuzfm 2020-02-03
Ingram 2020-01-16
缘起宇轩阁 2019-12-30
gongruitao 2020-01-04
zuixin 2020-01-03
pengkunstone 2019-12-27
waitui00 2019-12-22