單例模式:即某個類在程序運行過程當中只被實例化一次,也就是說該類在程序的生存週期裏只有一個實例對象。
使用單例模式好處:因爲這個類只實例化一次,無論多少個類中用到了這個類,也都只有一個該類的對象。所以,
減小了類實例對象的建立-->減少了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; }
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)
我在項目中碰到一個這樣的場景:
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); } } }