Heim  >  Artikel  >  Java  >  So lösen Sie Probleme mit dem Java-Singleton-Modus und der Thread-Sicherheit

So lösen Sie Probleme mit dem Java-Singleton-Modus und der Thread-Sicherheit

王林
王林nach vorne
2023-05-12 23:07:041401Durchsuche

    Einzelfallmodus, Mehrinstanzmodus und Thread-Sicherheit

    Einzelfallmodus

    Einzelfallmodus bedeutet, sicherzustellen, dass eine Klasse nur eine eindeutige Instanz hat und einen globalen Zugriffspunkt bereitstellt.

    Kategorie: Lazy Man Style, Hungry Man Style

    Warum brauchen wir den Singleton-Modus?

    In einigen Sonderfällen ist es erforderlich, dass eine Klasse nur zum Generieren eines eindeutigen Objekts verwendet werden kann. Beispiel: Es gibt viele Drucker im Druckerraum, aber das Druckverwaltungssystem verfügt nur über ein Druckaufgaben-Steuerungsobjekt, das die Druckwarteschlange verwaltet und jedem Drucker Druckaufgaben zuweist. Um solche Anforderungen zu erfüllen, wurde das Singleton-Muster erstellt.

    Implementierungsidee:

    Um zu verhindern, dass der Client den Konstruktor zum Erstellen mehrerer Objekte verwendet, deklarieren Sie den Konstruktor als privaten Typ. Dadurch ist diese Klasse jedoch nicht mehr verfügbar. Daher muss eine statische Methode bereitgestellt werden, die eine Instanz abrufen kann, normalerweise getInstance-Methode genannt, die eine Instanz zurückgibt. Diese Methode muss statisch sein, da statische Methoden basierend auf dem Klassennamen aufgerufen werden, andernfalls können sie nicht verwendet werden. „Klassendiagramm: Fauler chinesischer Stil“ reee

    Test-Singleton-Klasse: Cat das erste Mal Singleton-Objekte werden erstellt, wenn die statische Methode getInstance() verwendet wird.

    Der Hungry-Stil besteht darin, beim Laden der Klasse ein Singleton-Objekt zu erstellen, dh die Singleton-Klasse zu instanziieren, wenn ein statisches Singleton-Objekt deklariert wird.

    So lösen Sie Probleme mit dem Java-Singleton-Modus und der Thread-Sicherheit

    Thread-Sicherheit

    Der Lazy-Stil ist Thread-unsicher, während der Hungrige Stil Thread-sicher ist (wird weiter unten getestet).

    So lösen Sie Probleme mit dem Java-Singleton-Modus und der Thread-SicherheitRessourcenbelegung

    Der Lazy-Stil wird erstellt, wenn er verwendet wird, während der Hungrig-Stil erstellt wird, wenn die Klasse geladen wird. Daher ist der Stil des faulen Mannes nicht so schnell wie der Stil des hungrigen Mannes, aber der Stil des hungrigen Mannes beansprucht mehr Ressourcen. Wenn er nicht ständig verwendet wird, wird er viele Ressourcen beanspruchen. Sicherheit im Multithread-Modus Hier beteiligt Im Wettbewerb können die Ergebnisse mehrmals gleich sein (mehrmals dasselbe bedeutet nicht, dass es absolut korrekt ist), aber solange Sie mehrmals testen, können Sie unterschiedliche Ergebnisse sehen.

    Erklärung

    Hier verwende ich eine kleine Sammlungstechnik unter Verwendung der Eigenschaften der Set-Sammlung, um die jedes Mal generierten Hundeobjekte in der Set-Sammlung zu speichern und schließlich einfach die Größe aufzurufen( ) Methode der Sammlung Das ist es. Es ist ersichtlich, dass zwei Hundeobjekte generiert werden, was bedeutet, dass ein Fehler aufgetreten ist, bei dem es sich um einen Programmierfehler handelt. Es ist auch wichtig zu verstehen, dass bei Multithreading möglicherweise keine Fehler auftreten, sodass die generierten Hundeobjekte kleiner sind als die Anzahl der Threads. Da der Singleton im Hungry-Stil threadsicher ist, wird er hier nicht getestet. Wenn Sie interessiert sind, können Sie ihn testen.

    Die Lösung für die Sicherheit von Lazy-Singleton-Threads: SynchronisierungSo lösen Sie Probleme mit dem Java-Singleton-Modus und der Thread-Sicherheit

    Hinweis: Es gibt viele Synchronisierungsmethoden, und Sie können auch Lock für die Verarbeitung verwenden, nicht speziell das Schlüsselwort synchronisiert. Interessierte können mehr erfahren .

    Und die Synchronisationsmethode ist in der Regel langsamer und muss hinsichtlich der Leistung abgewogen werden.

    //懒汉式
    public class Dog {
    	private static Dog dog;
    	private String name;
    	private int age;
    	
    	//私有的构造器
    	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;
    	}
    
    	//静态工厂方法
    	public static Dog getInstance() {
    		if (dog == null) {
    			dog = new Dog();
    		}
    		return dog;
    	}
    
    	@Override
    	public String toString() {
    		return "Dog [name=" + name + ", age=" + age + "]";
    	}
    }

    Multi-Instanz-ModusHier ist ein Multi-Instanz-Modus, das heißt, die Anzahl der Objekte ist eine feste Zahl. Sie können die Förderung des Singleton-Musters sehen. Natürlich gibt es viele Möglichkeiten, es umzusetzen. Hier ist mein Weg.

    Multi-Instanzmodus-Klasse

    //饿汉式
    public class Cat {
    	private static Cat cat = new Cat();
    	private String name;
    	private int age;
    	
    	//私有构造器
    	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;
    	}
    
    	//静态工厂方法
    	public static Cat getInstance() {
    		return cat;
    	}
    
    	@Override
    	public String toString() {
    		return "Cat [name=" + name + ", age=" + age + "]";
    	}
    }

    Testklasse

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

    Laufergebnisse

    Hinweis: Bei Verwendung in einer Multithread-Umgebung muss auch die Thread-Sicherheit berücksichtigt werden. Wenn Sie Interesse haben, können Sie es selbst umsetzen.

    Ist das Singleton-Muster unbedingt sicher?

    Nicht unbedingt, es gibt viele Möglichkeiten, das Singleton-Muster zu durchbrechen!

    这里举例看一看(我只能举我知道的哈!其他的感兴趣,可以去探究一下!)
    使用反射:这种办法是非常有用的,通过反射即使是私有的属性和方法也可以访问了,因此反射破坏了类的封装性,所以使用反射还是要多多小心。但是反射也有许多其他的用途,这是一项非常有趣的技术(我也只是会一点点)。

    使用反射破坏单例模式测试类

    这里使用的还是前面的 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();
    		//设置可访问权限
    		con.setAccessible(true);
    		Dog dog1 = (Dog) con.newInstance();
    		Dog dog2 = (Dog) con.newInstance();
    		System.out.println(dog1 == dog2);
    	}
    }

    说明:反射的功能是很强大的,从这里既可以看出来,正是有了反射,才使得Java 语言具有了更多的特色,这也是Java的强大之处。

    使用对象序列化破坏单例模式

    测试实体类:Dog(增加一个对象序列化接口实现)

    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;
    	
    	//私有的构造器
    	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;
    	}
    
    	//静态工厂方法
    	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));
    		
    	}
    }

    运行结果

    So lösen Sie Probleme mit dem Java-Singleton-Modus und der Thread-Sicherheit

    说明
    这里可以看出来通过对象序列化(这里也可以说是对象的深拷贝或深克隆),
    同样也可以实现类的实例的不唯一性。这同样也算是破坏了类的封装性。对象序列化和反序列化的过程中,对象的唯一性变了。

    这里具体的原因很复杂,我最近看了点深拷贝的知识,所以只是知其然不知其之所以然。(所以学习是需要不断进行的!加油诸位。)
    这里我贴一下别的经验吧:(感兴趣的可以实现一下!)

    为什么序列化可以破坏单例了?
    答:序列化会通过反射调用无参数的构造方法创建一个新的对象。

    这个东西目前超出了我的能力范围了,但也是去查看源码得出来的,就是序列化(serializable)和反序列化(externalizable)接口的详细情况了。但是有一点,它也是通过反射来做的的,所以可以看出**反射(reflect)**是一种非常强大和危险的技术了。

    Das obige ist der detaillierte Inhalt vonSo lösen Sie Probleme mit dem Java-Singleton-Modus und der Thread-Sicherheit. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen