Mod Singleton merujuk kepada memastikan kelas hanya Ada ialah contoh yang unik dan menyediakan pusat akses global.
Kategori: Gaya Lelaki Malas, Gaya Lelaki Lapar
Mengapa anda memerlukan mod singleton?
Dalam beberapa kes khas, kelas hanya boleh digunakan untuk menjana objek unik. Contohnya: terdapat banyak pencetak di dalam bilik pencetak, tetapi sistem pengurusan cetakannya hanya mempunyai satu objek kawalan tugas cetakan, yang menguruskan baris gilir cetakan dan memberikan tugas cetakan kepada setiap pencetak. Corak tunggal dicipta untuk menyelesaikan keperluan tersebut.
Idea pelaksanaan:
Untuk mengelakkan klien daripada menggunakan pembina untuk mencipta berbilang objek, isytiharkan pembina sebagai jenis persendirian. Tetapi ini akan menjadikan kelas ini tidak tersedia, jadi kaedah statik yang boleh mendapatkan tika mesti disediakan, biasanya dipanggil kaedah getInstance, yang mengembalikan tika. Kaedah ini mestilah statik, kerana kaedah statik dipanggil berdasarkan nama kelas, jika tidak, kaedah ini tidak boleh digunakan.
Rajah kelas: Gaya Cina malas
Rajah kelas: Gaya Cina Lapar
Mari kita lihat contoh mudah dahulu:
Ujian kelas singleton: Dog’
//懒汉式 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 + "]"; } }
Ujian kelas singleton : Kucing
//饿汉式 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 + "]"; } }
Kelas ujian
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)); } }
Hasil lari
Perbezaan ciptaan
Gaya Cina malas mencipta singleton apabila statikkaedah getInstance() dipanggil untuk objek kali pertama.
Gaya Lapar adalah untuk mencipta objek tunggal apabila kelas dimuatkan, iaitu, membuat instantiat kelas tunggal apabila mengisytiharkan objek tunggal statik.
Thread-safe
Gaya malas adalah benang-tidak selamat, manakala gaya lapar adalah thread-safe (akan diuji di bawah).
Pekerjaan sumber
Gaya malas dicipta apabila ia digunakan, manakala gaya lapar dicipta apabila kelas dimuatkan. Oleh itu, gaya orang malas tidak sepantas gaya orang lapar, tetapi gaya orang lapar mengambil lebih banyak sumber Jika ia tidak digunakan sepanjang masa, ia akan menduduki banyak sumber.
Kelas berbilang benang
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); } }
Kelas ujian berbilang benang
rreeeKeputusan berjalan
Nota: Keputusan berbilang benang adalah sukar untuk diramalkan di sini Keputusan mungkin sama berbilang kali tidak bermakna ia adalah sama) Betul sekali), tetapi selagi anda mengujinya beberapa kali, anda boleh melihat hasil yang berbeza.
Penjelasan
Di sini saya menggunakan teknik set sedikit, menggunakan Set Ciri-ciri koleksi adalah untuk menyimpan objek anjing yang dijana setiap kali dalam koleksi Set, dan akhirnya hanya memanggil kaedah saiz() koleksi. Ia boleh dilihat bahawa dua objek anjing dihasilkan, yang bermaksud ralat telah berlaku, iaitu ralat pengaturcaraan. Ia juga penting untuk memahami bahawa ralat mungkin tidak berlaku di bawah berbilang benang, jadi objek anjing yang dijana adalah lebih kecil daripada bilangan utas.
Memandangkan singleton ala Hungry adalah selamat untuk benang, ia tidak akan diuji di sini Jika anda berminat, anda boleh mengujinya.
Satu cara untuk menyelesaikan keselamatan benang bagi singleton yang malas: penyegerakan
Nota: Terdapat banyak kaedah penyegerakan, dan anda juga boleh menggunakan Lock untuk pemprosesan ialah kaedah , bukan yang khusus Mereka yang berminat boleh meneroka lebih lanjut tentang kata kunci yang disegerakkan.
Dan kaedah penyegerakan biasanya lebih perlahan dan perlu ditimbang dari segi prestasi.
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()); } }
Berikut ialah mod berbilang tika, iaitu bilangan objek ialah nombor tetap. Anda boleh lihat promosi corak singleton. Sudah tentu, terdapat banyak cara untuk melaksanakannya. Anda boleh mencuba yang berikut.
Kelas mod berbilang contoh
//静态同步工厂方法 public synchronized static Dog getInstance() { if (dog == null) { dog = new Dog(); } return dog; }
Kelas ujian
//固定数目实例模式 public class MultiInstance { //实例数量,这里为四个 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() { //注意数组的下标只能为 COUNT - 1 if (MultiInstance.COUNT <= MultiInstance.INSTANCE_COUNT - 1) { instance[MultiInstance.COUNT] = new MultiInstance(); MultiInstance.COUNT++; } //返回实例前,执行了 COUNT++ 操作,所以 应该返回上一个实例 return MultiInstance.instance[MultiInstance.COUNT-1]; } }
Hasil berjalan
Nota : Jika digunakan dalam persekitaran berbilang benang, keselamatan benang juga mesti dipertimbangkan. Jika anda berminat, anda boleh melaksanakannya sendiri.
Adakah mod singleton semestinya selamat?
Tidak semestinya, terdapat banyak cara untuk memecahkan corak singleton!
这里举例看一看(我只能举我知道的哈!其他的感兴趣,可以去探究一下!)
使用反射:这种办法是非常有用的,通过反射即使是私有的属性和方法也可以访问了,因此反射破坏了类的封装性,所以使用反射还是要多多小心。但是反射也有许多其他的用途,这是一项非常有趣的技术(我也只是会一点点)。
使用反射破坏单例模式测试类
这里使用的还是前面的 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)); } }
运行结果
说明
这里可以看出来通过对象序列化(这里也可以说是对象的深拷贝或深克隆),
同样也可以实现类的实例的不唯一性。这同样也算是破坏了类的封装性。对象序列化和反序列化的过程中,对象的唯一性变了。
这里具体的原因很复杂,我最近看了点深拷贝的知识,所以只是知其然不知其之所以然。(所以学习是需要不断进行的!加油诸位。)
这里我贴一下别的经验吧:(感兴趣的可以实现一下!)
为什么序列化可以破坏单例了?
答:序列化会通过反射调用无参数的构造方法创建一个新的对象。
这个东西目前超出了我的能力范围了,但也是去查看源码得出来的,就是序列化(serializable)和反序列化(externalizable)接口的详细情况了。但是有一点,它也是通过反射来做的的,所以可以看出**反射(reflect)**是一种非常强大和危险的技术了。
Atas ialah kandungan terperinci Bagaimana untuk menyelesaikan masalah keselamatan java singleton dan thread. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!