單例模式在項目實戰中的幾個應用

1、單例模式簡單理解

單例模式:即某個類在程序運行過程當中只被實例化一次,也就是說該類在程序的生存週期裏只有一個實例對象。
使用單例模式好處:因爲這個類只實例化一次,無論多少個類中用到了這個類,也都只有一個該類的對象。所以,
減小了類實例對象的建立-->減少了GC壓力-->提高了程序的性能。前端

2、單例模式的幾種常見寫法

/**
 * 餓漢式(線程安全)。類加載時就建立惟一的單例實例,無論後面用不用都建立了再說
 * 空間換時間的思想
 */
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;
    }

3、單例模式在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(...)等

使用起來就很簡單了,好比:java

String value = Redis.getInstance().get(String key)
//或者是
Redis redisObj = Redis.getInstance()
String value = redisObj.get(String key)

4、單例模式在線程池建立中的使用

我在項目中碰到一個這樣的場景:
1)某個接口的併發請求較大;
2)對收到的數據要進行復雜的驗證及數據庫相關操做;
3)響應速度不能太慢,至少得2秒內吧;
因而正好能夠拿線程池來練練手,下面分享一下個人練手代碼(項目實戰中根據需求稍做修改便可應用):redis

本人實際項目中親測,而且使用JMeter作了壓測,吞吐量大,響應速度巨快!數據庫

1 任務類(這是一個實現Callable的線程任務,由於我須要返回結果)apache

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);
        }
    }
}

相關文章
相關標籤/搜索