欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談單例模式和線程安全問題

 更新時間:2023年04月07日 09:02:53   作者:CrazyDragon_King  
這篇文章主要介紹了淺談單例模式和線程安全問題,再某些特殊的情況下,存在一個類僅能用來產(chǎn)生一個唯一對象的必要性,因此需要單例模式,需要的朋友可以參考下

單例模式、多實例模式、和線程安全

單例模式

單例模式是指確保一個類僅有一個唯一的實例,并且提供了一個全局的訪問點。

分類: 懶漢式、餓漢式

為什么需要單例模式?

再某些特殊的情況下,存在一個類僅能用來產(chǎn)生一個唯一對象的必要性。例如:打印機室有許多打印機,但是它的打印管理系統(tǒng)只有一個打印任務(wù)控制對象,該對象管理打印排隊并分配打印任務(wù)給各個打印機。單例模式正是為了解決這樣的需求而產(chǎn)生的。

實現(xiàn)思路:

為了防止客戶端利用構(gòu)造器創(chuàng)建多個對象,將構(gòu)造方法聲明為 private 類型。但這樣會使得這個類不可用,所以必須提供一個可以獲得實例的靜態(tài)方法,通常稱為 getInstance 方法, 該方法返回一個實例。這個方法必須是靜態(tài)的,因為靜態(tài)方法是根據(jù)類名調(diào)用的,否則也是無法使用的。

類圖:懶漢式

在這里插入圖片描述

類圖:餓漢式

在這里插入圖片描述

先來看一個簡單的例子:

測試單例類:Dog’

//懶漢式
public class Dog {
	private static Dog dog;
	private String name;
	private int age;
	
	//私有的構(gòu)造器
	private Dog() {}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	//靜態(tài)工廠方法
	public static Dog getInstance() {
		if (dog == null) {
			dog = new Dog();
		}
		return dog;
	}

	@Override
	public String toString() {
		return "Dog [name=" + name + ", age=" + age + "]";
	}
}

測試單例類:Cat

//餓漢式
public class Cat {
	private static Cat cat = new Cat();
	private String name;
	private int age;
	
	//私有構(gòu)造器
	private Cat() {}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public int getAge() {
		return age;
	}
	
	public void setAge(int age) {
		this.age = age;
	}

	//靜態(tài)工廠方法
	public static Cat getInstance() {
		return cat;
	}

	@Override
	public String toString() {
		return "Cat [name=" + name + ", age=" + age + "]";
	}
}

測試類

import java.util.HashSet;
import java.util.Set;

public class Client {

	public static void main(String[] args) {
		//單線程模式測試
		Dog dog1 = Dog.getInstance();
		Dog dog2 = Dog.getInstance();
		System.out.println("dog1 == dog2: "+(dog1 == dog2));
		
		Cat cat1 = Cat.getInstance();
		Cat cat2 = Cat.getInstance();
		System.out.println("cat1 == cat2: "+(cat1 == cat2));
	}
}

運行結(jié)果

在這里插入圖片描述

懶漢式和餓漢式對比

創(chuàng)建區(qū)別

懶漢式是在第一次調(diào)用靜態(tài)方法 getInstance() 時創(chuàng)建單例對象。
餓漢式是在類加載時創(chuàng)建單例對象,即在聲明靜態(tài)單例對象時實例化單例類。

線程安全

懶漢式是線程不安全的,而餓漢式是線程安全的(下面會測試)。

資源占用

懶漢式是等到使用時才會創(chuàng)建,而餓漢式是在類加載時創(chuàng)建。所以懶漢式?jīng)]有餓漢式快,但是餓漢式比較占用資源,如果一直不使用,會很占據(jù)資源。

多線程模式下的安全性

多線程類

import java.util.HashSet;
import java.util.Set;

public class DogThread extends Thread{
	private Dog dog;
	private Set<Dog> set;
	
	public DogThread() {
		set = new HashSet<>();
	}
	
	//這個方法是為了測試添加的。
	public int getCount() {
		return set.size();
	}
	
	@Override
	public void run() {
		dog = Dog.getInstance();
		set.add(dog);
	}
}

多線程測試類

import java.util.HashSet;
import java.util.Set;

public class Client {

	public static void main(String[] args) {
		//單線程模式測試
		Dog dog1 = Dog.getInstance();
		Dog dog2 = Dog.getInstance();
		System.out.println("dog1 == dog2: "+(dog1 == dog2));
		
		Cat cat1 = Cat.getInstance();
		Cat cat2 = Cat.getInstance();
		System.out.println("cat1 == cat2: "+(cat1 == cat2));
		
		//多線程模式測試
		DogThread dogThread = new DogThread();
		Thread thread = null;
		for (int i = 0; i < 10; i++) {
			thread = new Thread(dogThread);
			thread.start();	
		}
		
		try {
			Thread.sleep(2000); //主線程等待子線程完成!
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("dog's number: "+dogThread.getCount());
	}
}

運行結(jié)果
注意:多線程的結(jié)果是很難預(yù)測的,這里涉及線程的競爭,可能多次運行結(jié)果是一樣的(多次一樣并不代表是絕對正確),但是只要多次測試,就能看到不一樣的結(jié)果。

在這里插入圖片描述

在這里插入圖片描述

說明

這里我使用一點集合的技巧,利用 Set 集合的特性,把每次產(chǎn)生的 dog 對象存入 Set集合中,最后只要調(diào)用集合的 size() 方法就行了??梢钥闯鰜懋a(chǎn)生了兩個 dog 對象,這就是產(chǎn)生了錯誤,這就是屬于編程錯誤了。還要明白多線程下不一定會出錯,所以產(chǎn)生的 dog 對象小于線程數(shù)。
由于 餓漢式單例 是線程安全的,這里就不測試了,有興趣的可以測試一下。

解決懶漢式單例線程安全的方法:同步
注意:同步有很多種方法,也可以使用 Lock 進行處理,同步是一種方法,不是特指 synchronzied 這個關(guān)鍵字,感興趣的人可以多探究一下。
并且同步的方法通常比較慢,性能方面也要權(quán)衡。

	//靜態(tài)同步工廠方法
	public synchronized static Dog getInstance() {
		if (dog == null) {
			dog = new Dog();
		}
		return dog;
	}

多實例模式

這里補充一個多實例的模式,就是對象數(shù)量是固定數(shù)目的。可以看出單例模式的推廣。當然了實現(xiàn)方式也有很多,大家可以嘗試以下,這里是我的方式。

多實例模式類

//固定數(shù)目實例模式
public class MultiInstance {
	//實例數(shù)量,這里為四個
	private final static int INSTANCE_COUNT = 4;
	private static int COUNT = 0;
	private static MultiInstance[] instance = new MultiInstance[4];
	
	private MultiInstance() {};
	
	public static MultiInstance getInstance() {
		//注意數(shù)組的下標只能為 COUNT - 1
		if (MultiInstance.COUNT <= MultiInstance.INSTANCE_COUNT - 1) {
			instance[MultiInstance.COUNT] = new MultiInstance();
			MultiInstance.COUNT++;
		}
		//返回實例前,執(zhí)行了 COUNT++ 操作,所以 應(yīng)該返回上一個實例
		return MultiInstance.instance[MultiInstance.COUNT-1];  
	}
}

測試類

import java.util.HashSet;
import java.util.Set;

public class Test {
	public static void main(String[] args) {
		
		System.out.println("------------------------");
		testMultiInstance();
	}

	//測試多實例模式(單例的擴展,固定數(shù)目實例)
	public static void testMultiInstance() {
		Set<MultiInstance> instanceSet = new HashSet<>();
		MultiInstance instance = null;
		for (int i = 0; i < 10; i++) {
			instance = MultiInstance.getInstance();
			instanceSet.add(instance);
		}
		System.out.println("8個實例中,不同的實例有:"+instanceSet.size());   
	}
}

運行結(jié)果
注意:如果在多線程環(huán)境下使用,也是要考慮線程安全的。感興趣的可以自己實現(xiàn)一下。

在這里插入圖片描述

單例模式一定是安全的嗎?

不一定,有很多方法可以破壞單例模式!

這里舉例看一看(我只能舉我知道的哈!其他的感興趣,可以去探究一下?。?br />使用反射:這種辦法是非常有用的,通過反射即使是私有的屬性和方法也可以訪問了,因此反射破壞了類的封裝性,所以使用反射還是要多多小心。但是反射也有許多其他的用途,這是一項非常有趣的技術(shù)(我也只是會一點點)。

使用反射破壞單例模式測試類

這里使用的還是前面的 Dog 實體類。注意我這里的**包名:**com。
所有的類都是在 com包 下面的。

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Client {
	public static void main(String[] args) throws 
	ClassNotFoundException, 
	NoSuchMethodException, 
	SecurityException, 
	InstantiationException, 
	IllegalAccessException, 
	IllegalArgumentException, 
	InvocationTargetException {
	
		Class<?> clazz = Class.forName("com.Dog");
		Constructor<?> con = clazz.getDeclaredConstructor();
		//設(shè)置可訪問權(quán)限
		con.setAccessible(true);
		Dog dog1 = (Dog) con.newInstance();
		Dog dog2 = (Dog) con.newInstance();
		System.out.println(dog1 == dog2);
	}
}

說明:反射的功能是很強大的,從這里既可以看出來,正是有了反射,才使得Java 語言具有了更多的特色,這也是Java的強大之處。

使用對象序列化破壞單例模式

測試實體類:Dog(增加一個對象序列化接口實現(xiàn))

import java.io.Serializable;
//懶漢式
public class Dog implements Serializable{
	private static final long serialVersionUID = 1L;
	
	private static Dog dog;
	private String name;
	private int age;
	
	//私有的構(gòu)造器
	private Dog() {}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	//靜態(tài)工廠方法
	public synchronized static Dog getInstance() {
		if (dog == null) {
			dog = new Dog();
		}
		return dog;
	}

	@Override
	public String toString() {
		return "Dog [name=" + name + ", age=" + age + "]";
	}
}

對象序列化測試類

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Client {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		Dog dog1 = Dog.getInstance();
		dog1.setName("小黑");
		dog1.setAge(2);
		System.out.println(dog1.toString());
		
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(dog1);
		
		
		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
		ObjectInputStream ois = new ObjectInputStream(bis);
		Dog dog2 = (Dog) ois.readObject();
		System.out.println(dog2.toString());
		System.out.println("dog1 == dog2: "+(dog1 == dog2));
		
	}
}

運行結(jié)果

在這里插入圖片描述

說明
這里可以看出來通過對象序列化(這里也可以說是對象的深拷貝或深克隆),
同樣也可以實現(xiàn)類的實例的不唯一性。這同樣也算是破壞了類的封裝性。對象序列化和反序列化的過程中,對象的唯一性變了。

這里具體的原因很復(fù)雜,我最近看了點深拷貝的知識,所以只是知其然不知其之所以然。(所以學(xué)習(xí)是需要不斷進行的!加油諸位。)
這里我貼一下別的經(jīng)驗吧:(感興趣的可以實現(xiàn)一下?。?/p>

為什么序列化可以破壞單例了?
答:序列化會通過反射調(diào)用無參數(shù)的構(gòu)造方法創(chuàng)建一個新的對象。

這個東西目前超出了我的能力范圍了,但也是去查看源碼得出來的,就是序列化(serializable)和反序列化(externalizable)接口的詳細情況了。但是有一點,它也是通過反射來做的的,所以可以看出**反射(reflect)**是一種非常強大和危險的技術(shù)了。

總結(jié)

單例模式 是很有趣的,它涉及了很多知識,所以大家學(xué)習(xí)的時候,不要只滿足與課本的知識,如果只是會使用簡單的 單例模式,那是沒有什么核心競爭力的,任何一個知識,只要往下深究都是不容易的,我也只是一個初學(xué)者,希望和大家一起努力進步。

到此這篇關(guān)于淺談單例模式和線程安全問題的文章就介紹到這了,更多相關(guān)單例模式和線程安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java動態(tài)代理示例分享

    java動態(tài)代理示例分享

    這篇文章主要介紹了java動態(tài)代理示例,需要的朋友可以參考下
    2014-02-02
  • Java數(shù)據(jù)結(jié)構(gòu)與算法之棧(Stack)實現(xiàn)詳解

    Java數(shù)據(jù)結(jié)構(gòu)與算法之棧(Stack)實現(xiàn)詳解

    這篇文章主要為大家詳細介紹了Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)筆記第二篇,Java數(shù)據(jù)結(jié)構(gòu)與算法之棧Stack實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • RedisTemplate常用方法總結(jié)

    RedisTemplate常用方法總結(jié)

    本文主要介紹了RedisTemplate常用方法總結(jié),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 深入解析Java多態(tài)進階學(xué)習(xí)

    深入解析Java多態(tài)進階學(xué)習(xí)

    java的動態(tài)綁定機制非常重要。這篇文章將帶大家更深入的學(xué)習(xí)一下Java的多態(tài),文中的示例代碼講解詳細,對我們學(xué)習(xí)Java有一定幫助,需要的可以參考一下
    2022-07-07
  • java實現(xiàn)在原有日期時間上加幾個月或幾天

    java實現(xiàn)在原有日期時間上加幾個月或幾天

    這篇文章主要介紹了java實現(xiàn)在原有日期時間上加幾個月或幾天,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Spring使用xml方式整合第三方框架流程詳解

    Spring使用xml方式整合第三方框架流程詳解

    這篇文章主要介紹了Spring使用xml方式整合第三方框架流程,Spring會在應(yīng)用上下文中為某個bean尋找其依賴的bean,Spring中bean有三種裝配機制,分別是:在xml中顯式配置、在java中顯式配置、隱式的bean發(fā)現(xiàn)機制和自動裝配
    2023-02-02
  • 一步步教你如何使用Java實現(xiàn)WebSocket

    一步步教你如何使用Java實現(xiàn)WebSocket

    websocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議,它實現(xiàn)了瀏覽器與服務(wù)器的全雙工通訊-允許服務(wù)器主動發(fā)起信息個客戶端,websocket是一種持久協(xié)議,http是非持久協(xié)議,下面這篇文章主要給大家介紹了關(guān)于如何使用Java實現(xiàn)WebSocket的相關(guān)資料,需要的朋友可以參考下
    2023-05-05
  • Java?ynchronized重量級鎖的核心原理詳解

    Java?ynchronized重量級鎖的核心原理詳解

    這篇文章主要為大家詳細介紹了Java?ynchronized重量級鎖的核心原理,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • Maven中怎么手動添加jar包到本地倉庫詳解(repository)

    Maven中怎么手動添加jar包到本地倉庫詳解(repository)

    這篇文章主要給大家介紹了關(guān)于Maven中怎么手動添加jar包到本地倉庫的相關(guān)資料,文中通過圖文以及實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-04-04
  • Java通俗易懂系列設(shè)計模式之責任鏈模式

    Java通俗易懂系列設(shè)計模式之責任鏈模式

    這篇文章主要介紹了Java通俗易懂系列設(shè)計模式之責任鏈模式,對設(shè)計模式感興趣的同學(xué),一定要看一下
    2021-04-04

最新評論