※產品缺大量曝光嗎?你需要的是一流包裝設計!
窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。
餓漢式
// 餓漢式單例
public class Hungry {
//構造器私有
private Hungry(){
}
// 一上來就把這個類加載了
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
// 餓漢式單例
public class Hungry {
// 這4組數據非常耗內存資源,餓漢式一上來就把所有的內存裏面的東西全部加載進來了,就存在這個空間
// 但這個空間現在是沒有使用的,可能會造成浪費空間
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
//構造器私有
private Hungry(){
}
// 一上來就把這個類加載了
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
餓漢式單例可能會造成浪費空間,所以想要用的時候再去創建這個對象,平時就先放在這個地方,於是就出現了懶漢式!
懶漢式
// 懶漢式單例
public class LazyMan {
// 構造器私有
private LazyMan(){
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if (lazyMan==null){
lazyMan = new LazyMan();
}
return lazyMan;
}
}
它是有問題的,單線程下確實單例ok,多線程併發就會出現問題!
測試
// 懶漢式單例
public class LazyMan {
// 構造器私有
private LazyMan(){
System.out.println(Thread.currentThread().getName()+":: ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if (lazyMan==null){
lazyMan = new LazyMan();
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan.getInstance();
}).start();
}
}
}
發現單例有問題,每次結果可能都不一樣!
解決
// 懶漢式單例
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+":: ok");
}
private static LazyMan lazyMan;
// 雙重檢測鎖模式的 懶漢式單例 DCL懶漢式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10 ; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
但在極端情況下還是可能出現問題
經歷三個步驟:
1、 分配內存空間
2、 執行構造方法,初始化對象
3、 把這個對象指向這個空間
有可能會發生指令重排的操作!
比如,期望它執行 123 ,但是它真實可能執行132,比如第一個A線程過來執行了132,先分配空間再吧這個空間佔用了,佔用之後再去執行構造方法,如果現在突然來了個B線程,由於A已經指向這個空間了,它會以為這個 lazyMan 不等於 null ,直接return ,此時lazyMan還沒有完成構造,所以必須避免這個問題!
必須加上volatile
// 懶漢式單例
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+":: ok");
}
// 避免指令重排
private volatile static LazyMan lazyMan;
// 雙重檢測鎖模式的 懶漢式單例 DCL懶漢式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10 ; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
靜態內部類
// 靜態內部類
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
也是單例模式的一種,不安全!
單例不安全 反射
// 懶漢式單例
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()+":: ok");
}
private volatile static LazyMan lazyMan;
// 雙重檢測鎖模式的 懶漢式單例 DCL懶漢式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan(); //不是一個原子性操作
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
LazyMan instance1 = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); // 無視了私有的構造器
// 通過反射創建對象
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
結論:反射可以破壞這種單例
解決
// 懶漢式單例
public class LazyMan {
private LazyMan(){
synchronized (LazyMan.class){
if (lazyMan!=null){
throw new RuntimeException("不要試圖使用反射破環 異常");
}
}
System.out.println(Thread.currentThread().getName()+":: ok");
}
private volatile static LazyMan lazyMan;
// 雙重檢測鎖模式的 懶漢式單例 DCL懶漢式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan(); //不是一個原子性操作
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
LazyMan instance1 = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); // 無視了私有的構造器
// 通過反射創建對象
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
但是如果都用反射創建對象的情況下,還是會破環單例!
測試
※南投搬家公司費用需注意的眉眉角角,別等搬了再說!
上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務
解決
// 懶漢式單例
public class LazyMan {
// 標誌位
private static boolean abc = false;
private LazyMan(){
synchronized (LazyMan.class){
if (abc==false){
abc=true;
}else {
throw new RuntimeException("不要試圖使用反射破環 異常");
}
}
System.out.println(Thread.currentThread().getName()+":: ok");
}
private volatile static LazyMan lazyMan;
// 雙重檢測鎖模式的 懶漢式單例 DCL懶漢式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan(); //不是一個原子性操作
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
//LazyMan instance1 = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); // 無視了私有的構造器
// 通過反射創建對象
LazyMan instance2 = declaredConstructor.newInstance();
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
但是如果被人知道 abc
這個變量,也可以破環!
單例又被破環了!
看一下源碼
它說不能使用反射破環枚舉,枚舉是jdk1.5出現的,自帶單例模式!
測試,寫一個枚舉類
// enum 本身就是一個class類
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
查看它的源碼
試圖破環!
// enum 本身就是一個class類
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
它竟然說我現在的這個枚舉類中沒有空參構造器!
然後就去源碼里分析!
找到這個class文件!利用javap反編譯一下!
發現這個也显示有一個空參構造,證明這個也不對,用第三方的工具查看!
利用它再吧class文件生成java文件!
打開這個java文件
證明是idea和源碼騙了我!
再次嘗試破環!
// enum 本身就是一個class類
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
結論:反射無法破環枚舉類!
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。