日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

注册式单例

發布時間:2024/4/13 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 注册式单例 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

將每一個實例都緩存到統一的容器中,使用唯一標識獲取實例

//常量中去使用,常量不就是用來大家都能夠共用嗎? //通常在通用API中使用 public enum EnumSingleton {INSTANCE;private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}public static EnumSingleton getInstance(){return INSTANCE;} } public class EnumSingletonTest { // public static void main(String[] args) { // try { // EnumSingleton instance1 = null; // // EnumSingleton instance2 = EnumSingleton.getInstance(); // instance2.setData(new Object()); // // FileOutputStream fos = new FileOutputStream("EnumSingleton.obj"); // ObjectOutputStream oos = new ObjectOutputStream(fos); // oos.writeObject(instance2); // oos.flush(); // oos.close(); // // FileInputStream fis = new FileInputStream("EnumSingleton.obj"); // ObjectInputStream ois = new ObjectInputStream(fis); // instance1 = (EnumSingleton) ois.readObject(); // ois.close(); // // System.out.println(instance1.getData()); // System.out.println(instance2.getData()); // System.out.println(instance1.getData() == instance2.getData()); // // }catch (Exception e){ // e.printStackTrace(); // } // }public static void main(String[] args) {try {Class clazz = EnumSingleton.class;Constructor c = clazz.getDeclaredConstructor(String.class,int.class);c.setAccessible(true);EnumSingleton enumSingleton = (EnumSingleton)c.newInstance("Tom",666);}catch (Exception e){e.printStackTrace();}}} static {INSTANCE = new EnumSingleton("INSTANCE", 0);$VALUES = (new EnumSingleton[] {INSTANCE}); }

原來,枚舉式單例在靜態代碼塊中就給INSTANCE 進行了賦值,是餓漢式單例的實現。至此,我們還可以試想,序列化我們能否破壞枚舉式單例呢?我們不妨再來看一下JDK源碼,還是回到ObjectInputStream 的readObject0()方法:

private Object readObject0(boolean unshared) throws IOException {...case TC_ENUM:return checkResolve(readEnum(unshared));... }

我們看到在readObject0()中調用了readEnum()方法,來看readEnum()中代碼實現:

case TC_ENUM:return checkResolve(readEnum(unshared)); private Enum<?> readEnum(boolean unshared) throws IOException {if (bin.readByte() != TC_ENUM) {throw new InternalError();}ObjectStreamClass desc = readClassDesc(false);if (!desc.isEnum()) {throw new InvalidClassException("non-enum class: " + desc);}int enumHandle = handles.assign(unshared ? unsharedMarker : null);ClassNotFoundException resolveEx = desc.getResolveException();if (resolveEx != null) {handles.markException(enumHandle, resolveEx);}String name = readString(false);Enum<?> result = null;Class<?> cl = desc.forClass();if (cl != null) {try {@SuppressWarnings("unchecked")Enum<?> en = Enum.valueOf((Class)cl, name);result = en;} catch (IllegalArgumentException ex) {throw (IOException) new InvalidObjectException("enum constant " + name + " does not exist in " +cl).initCause(ex);}if (!unshared) {handles.setObject(enumHandle, result);}}handles.finish(enumHandle);passHandle = enumHandle;return result; }

我們發現枚舉類型其實通過類名和Class 對象類找到一個唯一的枚舉對象。因此,枚舉對象不可能被類加載器加載多次。那么反射是否能破壞枚舉式單例呢?來看一段測試代碼:

Enum<?> en = Enum.valueOf((Class)cl, name); public static void main(String[] args) {try {Class clazz = EnumSingleton.class;Constructor c = clazz.getDeclaredConstructor(String.class,int.class);c.setAccessible(true);EnumSingleton enumSingleton = (EnumSingleton)c.newInstance("Tom",666);}catch (Exception e){e.printStackTrace();} }

報的是java.lang.NoSuchMethodException 異常,意思是沒找到無參的構造方法。這時候,我們打開java.lang.Enum 的源碼代碼,查看它的構造方法,只有一個protected的構造方法,代碼如下:

protected Enum(String name, int ordinal) {this.name = name;this.ordinal = ordinal; }

這時錯誤已經非常明顯了,告訴我們Cannot reflectively create enum objects,不能用反射來創建枚舉類型。還是習慣性地想來看看JDK 源碼,進入Constructor 的newInstance()方法:

@CallerSensitive public T newInstance(Object ... initargs)throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException {if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class<?> caller = Reflection.getCallerClass();checkAccess(caller, clazz, null, modifiers);}}if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");ConstructorAccessor ca = constructorAccessor; // read volatileif (ca == null) {ca = acquireConstructorAccessor();}@SuppressWarnings("unchecked")T inst = (T) ca.newInstance(initargs);return inst; }

在newInstance()方法中做了強制性的判斷,如果修飾符是Modifier.ENUM 枚舉類型,直接拋出異常。到這為止,我們是不是已經非常清晰明了呢?枚舉式單例也是《Effective Java》書中推薦的一種單例實現寫法。在JDK 枚舉的語法特殊性,以及反射也為枚舉保駕護航,讓枚舉式單例成為一種比較優雅的實現。
接下來看注冊式單例還有另一種寫法,容器緩存的寫法,創建ContainerSingleton 類:

//Spring中的做法,就是用這種注冊式單例 public class ContainerSingleton {private ContainerSingleton(){}private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();public static Object getInstance(String className){synchronized (ioc) {if (!ioc.containsKey(className)) {Object obj = null;try {obj = Class.forName(className).newInstance();ioc.put(className, obj);} catch (Exception e) {e.printStackTrace();}return obj;} else {return ioc.get(className);}}} }

容器式寫法適用于創建實例非常多的情況,便于管理。但是,是非線程安全的。到此,注冊式單例介紹完畢。我們還可以來看看Spring 中的容器式單例的實現代碼:

public class ContainerSingletonTest {public static void main(String[] args) {try {long start = System.currentTimeMillis();ConcurrentExecutor.execute(new ConcurrentExecutor.RunHandler() {public void handler() {Object obj = ContainerSingleton.getInstance("com.leon.pattern.singleton.Pojo");;System.out.println(System.currentTimeMillis() + ": " + obj);}}, 10,6);long end = System.currentTimeMillis();System.out.println("總耗時:" + (end - start) + " ms.");}catch (Exception e){e.printStackTrace();}} } public class Pojo { } public class ConcurrentExecutor {/*** @param runHandler* @param executeCount 發起請求總數* @param concurrentCount 同時并發執行的線程數* @throws Exception*/public static void execute(final RunHandler runHandler,int executeCount,int concurrentCount) throws Exception {ExecutorService executorService = Executors.newCachedThreadPool();//控制信號量,此處用于控制并發的線程數final Semaphore semaphore = new Semaphore(concurrentCount);//閉鎖,可實現計數量遞減final CountDownLatch countDownLatch = new CountDownLatch(executeCount);for (int i = 0; i < executeCount; i ++){executorService.execute(new Runnable() {public void run() {try{//執行此方法用于獲取執行許可,當總計未釋放的許可數不超過executeCount時,//則允許同性,否則線程阻塞等待,知道獲取到許可semaphore.acquire();runHandler.handler();//釋放許可semaphore.release();}catch (Exception e){e.printStackTrace();}countDownLatch.countDown();}});}countDownLatch.await();//線程阻塞,知道閉鎖值為0時,阻塞才釋放,繼續往下執行executorService.shutdown();}public interface RunHandler{void handler();} }

?

總結

以上是生活随笔為你收集整理的注册式单例的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。