最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談單例模式和線程安全問(wèn)題

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

單例模式、多實(shí)例模式、和線程安全

單例模式

單例模式是指確保一個(gè)類僅有一個(gè)唯一的實(shí)例,并且提供了一個(gè)全局的訪問(wèn)點(diǎn)。

分類: 懶漢式、餓漢式

為什么需要單例模式?

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

實(shí)現(xiàn)思路:

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

類圖:懶漢式

在這里插入圖片描述

類圖:餓漢式

在這里插入圖片描述

先來(lái)看一個(gè)簡(jiǎn)單的例子:

測(cè)試單例類: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 + "]";
	}
}

測(cè)試單例類: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 + "]";
	}
}

測(cè)試類

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

public class Client {

	public static void main(String[] args) {
		//單線程模式測(cè)試
		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));
	}
}

運(yùn)行結(jié)果

在這里插入圖片描述

懶漢式和餓漢式對(duì)比

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

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

線程安全

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

資源占用

懶漢式是等到使用時(shí)才會(huì)創(chuàng)建,而餓漢式是在類加載時(shí)創(chuàng)建。所以懶漢式?jīng)]有餓漢式快,但是餓漢式比較占用資源,如果一直不使用,會(huì)很占據(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<>();
	}
	
	//這個(gè)方法是為了測(cè)試添加的。
	public int getCount() {
		return set.size();
	}
	
	@Override
	public void run() {
		dog = Dog.getInstance();
		set.add(dog);
	}
}

多線程測(cè)試類

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

public class Client {

	public static void main(String[] args) {
		//單線程模式測(cè)試
		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));
		
		//多線程模式測(cè)試
		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());
	}
}

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

在這里插入圖片描述

在這里插入圖片描述

說(shuō)明

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

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

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

多實(shí)例模式

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

多實(shí)例模式類

//固定數(shù)目實(shí)例模式
public class MultiInstance {
	//實(shí)例數(shù)量,這里為四個(gè)
	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ù)組的下標(biāo)只能為 COUNT - 1
		if (MultiInstance.COUNT <= MultiInstance.INSTANCE_COUNT - 1) {
			instance[MultiInstance.COUNT] = new MultiInstance();
			MultiInstance.COUNT++;
		}
		//返回實(shí)例前,執(zhí)行了 COUNT++ 操作,所以 應(yīng)該返回上一個(gè)實(shí)例
		return MultiInstance.instance[MultiInstance.COUNT-1];  
	}
}

測(cè)試類

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

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

	//測(cè)試多實(shí)例模式(單例的擴(kuò)展,固定數(shù)目實(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個(gè)實(shí)例中,不同的實(shí)例有:"+instanceSet.size());   
	}
}

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

在這里插入圖片描述

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

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

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

使用反射破壞單例模式測(cè)試類

這里使用的還是前面的 Dog 實(shí)體類。注意我這里的**包名:**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è)置可訪問(wèn)權(quán)限
		con.setAccessible(true);
		Dog dog1 = (Dog) con.newInstance();
		Dog dog2 = (Dog) con.newInstance();
		System.out.println(dog1 == dog2);
	}
}

說(shuō)明:反射的功能是很強(qiáng)大的,從這里既可以看出來(lái),正是有了反射,才使得Java 語(yǔ)言具有了更多的特色,這也是Java的強(qiáng)大之處。

使用對(duì)象序列化破壞單例模式

測(cè)試實(shí)體類:Dog(增加一個(gè)對(duì)象序列化接口實(shí)現(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 + "]";
	}
}

對(duì)象序列化測(cè)試類

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

運(yùn)行結(jié)果

在這里插入圖片描述

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

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

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

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

總結(jié)

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

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

相關(guān)文章

  • SpringBoot獲取前臺(tái)參數(shù)的六種方式以及統(tǒng)一響應(yīng)

    SpringBoot獲取前臺(tái)參數(shù)的六種方式以及統(tǒng)一響應(yīng)

    本文主要介紹了SpringBoot獲取前臺(tái)參數(shù)的六種方式以及統(tǒng)一響應(yīng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 如何解決報(bào)錯(cuò):java.net.BindException:無(wú)法指定被請(qǐng)求的地址問(wèn)題

    如何解決報(bào)錯(cuò):java.net.BindException:無(wú)法指定被請(qǐng)求的地址問(wèn)題

    在Linux虛擬機(jī)上安裝并啟動(dòng)Tomcat時(shí)遇到啟動(dòng)失敗的問(wèn)題,通過(guò)檢查端口及配置文件未發(fā)現(xiàn)異常,后發(fā)現(xiàn)/etc/hosts文件中缺少localhost的映射,添加后重啟Tomcat成功,Tomcat啟動(dòng)時(shí)會(huì)檢查localhost的IP映射,缺失或錯(cuò)誤都可能導(dǎo)致啟動(dòng)失敗
    2024-10-10
  • Springboot主程序類注解配置過(guò)程圖解

    Springboot主程序類注解配置過(guò)程圖解

    這篇文章主要介紹了Springboot主程序類注解配置過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 2020JDK1.8安裝教程詳解(一次就可安裝成功)

    2020JDK1.8安裝教程詳解(一次就可安裝成功)

    這篇文章主要介紹了2020JDK1.8安裝教程詳解(一次就可安裝成功),本文通過(guò)圖文并茂的形式分步驟給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-08-08
  • 基于Java的打包jar、war、ear包的作用與區(qū)別詳解

    基于Java的打包jar、war、ear包的作用與區(qū)別詳解

    本篇文章,小編為大家介紹,基于Java的打包jar、war、ear包的作用與區(qū)別詳解。需要的朋友參考下
    2013-04-04
  • 詳解Java線性結(jié)構(gòu)中的鏈表

    詳解Java線性結(jié)構(gòu)中的鏈表

    除了一些算法之外,我們還有掌握一些常見(jiàn)的數(shù)據(jù)結(jié)構(gòu),比如數(shù)組、鏈表、棧、隊(duì)列、樹(shù)等結(jié)構(gòu),所以接下來(lái)就給大家詳細(xì)講解一下線性結(jié)構(gòu)中的鏈表,需要的朋友可以參考下
    2023-07-07
  • Java ArrayList使用總結(jié)

    Java ArrayList使用總結(jié)

    這篇文章主要介紹了Java ArrayList使用總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-03-03
  • Java如何通過(guò)線程解決生產(chǎn)者/消費(fèi)者問(wèn)題

    Java如何通過(guò)線程解決生產(chǎn)者/消費(fèi)者問(wèn)題

    這篇文章主要介紹了Java如何通過(guò)線程解決生產(chǎn)者/消費(fèi)者問(wèn)題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-10-10
  • IntelliJ IDEA之高效代碼插件RainBow Brackets詳解

    IntelliJ IDEA之高效代碼插件RainBow Brackets詳解

    這篇文章主要介紹了IntelliJ IDEA之高效代碼插件RainBow Brackets詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例

    SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例

    Graylog是一個(gè)生產(chǎn)級(jí)別的日志收集系統(tǒng),集成Mongo和Elasticsearch進(jìn)行日志收集,這篇文章主要介紹了SpringBoot使用Graylog日志收集的實(shí)現(xiàn)示例,感興趣的小伙伴們可以參考一下
    2019-04-04

最新評(píng)論

图木舒克市| 丽水市| 华阴市| 张北县| 固始县| 孝昌县| 濮阳县| 庐江县| 霞浦县| 平阴县| 甘南县| 庄河市| 屯门区| 贺州市| 罗城| 新源县| 云浮市| 册亨县| 资阳市| 辽源市| 永平县| 射阳县| 海晏县| 万载县| 正宁县| 龙游县| 郸城县| 始兴县| 名山县| 社旗县| 潮安县| 潞西市| 江永县| 宜阳县| 新野县| 凌云县| 民权县| 运城市| 宜兰市| 堆龙德庆县| 苍梧县|