「java写一个单例模式」如何写一个单例模式
今天给各位分享java写一个单例模式的知识,其中也会对如何写一个单例模式进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
java中的单例模式的代码怎么写
我从我的博客里把我的文章粘贴过来吧,对于单例模式模式应该有比较清楚的解释:
单例模式在我们日常的项目中十分常见,当我们在项目中需要一个这样的一个对象,这个对象在内存中只能有一个实例,这时我们就需要用到单例。
一般说来,单例模式通常有以下几种:
1.饥汉式单例
public class Singleton {
private Singleton(){};
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
这是最简单的单例,这种单例最常见,也很可靠!它有个唯一的缺点就是无法完成延迟加载——即当系统还没有用到此单例时,单例就会被加载到内存中。
在这里我们可以做个这样的测试:
将上述代码修改为:
public class Singleton {
private Singleton(){
System.out.println("createSingleton");
};
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
public static void testSingleton(){
System.out.println("CreateString");
}
}
而我们在另外一个测试类中对它进行测试(本例所有测试都通过Junit进行测试)
public class TestSingleton {
@Test
public void test(){
Singleton.testSingleton();
}
}
输出结果:
createSingleton
CreateString
我们可以注意到,在这个单例中,即使我们没有使用单例类,它还是被创建出来了,这当然是我们所不愿意看到的,所以也就有了以下一种单例。
2.懒汉式单例
public class Singleton1 {
private Singleton1(){
System.out.println("createSingleton");
}
private static Singleton1 instance = null;
public static synchronized Singleton1 getInstance(){
return instance==null?new Singleton1():instance;
}
public static void testSingleton(){
System.out.println("CreateString");
}
}
上面的单例获取实例时,是需要加上同步的,如果不加上同步,在多线程的环境中,当线程1完成新建单例操作,而在完成赋值操作之前,线程2就可能判
断instance为空,此时,线程2也将启动新建单例的操作,那么多个就出现了多个实例被新建,也就违反了我们使用单例模式的初衷了。
我们在这里也通过一个测试类,对它进行测试,最后面输出是
CreateString
可以看出,在未使用到单例类时,单例类并不会加载到内存中,只有我们需要使用到他的时候,才会进行实例化。
这种单例解决了单例的延迟加载,但是由于引入了同步的关键字,因此在多线程的环境下,所需的消耗的时间要远远大于第一种单例。我们可以通过一段测试代码来说明这个问题。
public class TestSingleton {
@Test
public void test(){
long beginTime1 = System.currentTimeMillis();
for(int i=0;i100000;i++){
Singleton.getInstance();
}
System.out.println("单例1花费时间:"+(System.currentTimeMillis()-beginTime1));
long beginTime2 = System.currentTimeMillis();
for(int i=0;i100000;i++){
Singleton1.getInstance();
}
System.out.println("单例2花费时间:"+(System.currentTimeMillis()-beginTime2));
}
}
最后输出的是:
单例1花费时间:0
单例2花费时间:10
可以看到,使用第一种单例耗时0ms,第二种单例耗时10ms,性能上存在明显的差异。为了使用延迟加载的功能,而导致单例的性能上存在明显差异,
是不是会得不偿失呢?是否可以找到一种更好的解决的办法呢?既可以解决延迟加载,又不至于性能损耗过多,所以,也就有了第三种单例:
3.内部类托管单例
public class Singleton2 {
private Singleton2(){}
private static class SingletonHolder{
private static Singleton2 instance=new Singleton2();
}
private static Singleton2 getInstance(){
return SingletonHolder.instance;
}
}
在这个单例中,我们通过静态内部类来托管单例,当这个单例被加载时,不会初始化单例类,只有当getInstance方法被调用的时候,才会去加载
SingletonHolder,从而才会去初始化instance。并且,单例的加载是在内部类的加载的时候完成的,所以天生对线程友好,而且也不需要
synchnoized关键字,可以说是兼具了以上的两个优点。
4.总结
一般来说,上述的单例已经基本可以保证在一个系统中只会存在一个实例了,但是,仍然可能会有其他的情况,导致系统生成多个单例,请看以下情况:
public class Singleton3 implements Serializable{
private Singleton3(){}
private static class SingletonHolder{
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
}
通过一段代码来测试:
@Test
public void test() throws Exception{
Singleton3 s1 = null;
Singleton3 s2 = Singleton3.getInstance();
//1.将实例串行话到文件
FileOutputStream fos = new FileOutputStream("singleton.txt");
ObjectOutputStream oos =new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
//2.从文件中读取出单例
FileInputStream fis = new FileInputStream("singleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (Singleton3) ois.readObject();
if(s1==s2){
System.out.println("同一个实例");
}else{
System.out.println("不是同一个实例");
}
}
输出:
不是同一个实例
可以看到当我们把单例反序列化后,生成了多个不同的单例类,此时,我们必须在原来的代码中加入readResolve()函数,来阻止它生成新的单例
public class Singleton3 implements Serializable{
private Singleton3(){}
private static class SingletonHolder{
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
//阻止生成新的实例
public Object readResolve(){
return SingletonHolder.instance;
}
}
再次测试时,就可以发现他们生成的是同一个实例了。
如何写一个标准的Java单例模式
一般Singleton模式通常有两种形式:第一种形式:也是常用的形式。publicclassSingleton{privatestaticSingletoninstance=null;privateSingleton(){//dosomething}//这个方法比下面的有所改进,不用每次都进行生成对象,只是第一次使用时生成实例,提高了效率publicstaticSingletongetInstance(){if(instance==null){instance=newSingleton();}returninstance;}}第二种形式:publicclassSingleton{//在自己内部定义自己的一个实例,只供内部调用privatestaticSingletoninstance=newSingleton();privateSingleton(){//dosomething}//这里提供了一个供外部访问本class的静态方法,可以直接访问publicstaticSingletongetInstance(){returninstance;}}
如何设计一个安全的单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
这种方式是懒汉式最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized ,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
饿汉式基于 classloader 机制避免了多线程的同步问题,不过, instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance() 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
综上所述,我们可以看出使用懒汉式的方式创建的单例模式在多线程下并不是安全的,所以提出了使用 双重校验锁 的方式来实现懒汉式的单例
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
我们使用反射获取了 Singleton 的无参构造器,通过调用 setAccessible(true) 忽视 private 权限控制符,成功的创建了 Singleton 的对象。反射创建对象的根本方法就是获取了 Singleton 的私有构造器,那么针对的方法也就很简单了,只需要我们在私有构造器中再次判断单例对象是否已经创建,若存在已创建的单例对象,则抛出运行时异常即可!
使用反射再次创建对象后运行截图如下:
可以看出单例模式又再次被破坏了。为了解决序列化和反序列化破坏单例模式的问题,我们可以定义一种称为 readResolve() 的特殊序列化方法。如果我们定义了 readResolve() 方法,那么在对象被序列化后就会被调用。
再次运行序列化和反序列化程序,可以观察到如下的结果
不过我们还有一种最简单的方式去创建一个单例模式,那就是使用 枚举 来创建单例模式
运行类 及 结果
通过静态内部类的方法创建单例同样实现了 懒加载 和 线程安全 ,因为 Java 在加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
如何在Java中实现单例模式?
单例模式大致有五种写法,分别为懒汉,恶汉,静态内部类,枚举和双重校验锁。
1、懒汉写法,常用写法
class LazySingleton{
private static LazySingleton singleton;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(singleton==null){
singleton=new LazySingleton();
}
return singleton;
}
}
2、恶汉写法,缺点是没有达到lazy loading的效果
class HungrySingleton{
private static HungrySingleton singleton=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return singleton;
}
}
3、静态内部类,优点:加载时不会初始化静态变量INSTANCE,因为没有主动使用,达到Lazy loading
class InternalSingleton{
private static class SingletonHolder{
private final static InternalSingleton INSTANCE=new InternalSingleton();
}
private InternalSingleton(){}
public static InternalSingleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
4、枚举,优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
enum EnumSingleton{
INSTANCE;
public void doSomeThing(){
}
}
5、双重校验锁,在当前的内存模型中无效
class LockSingleton{
private volatile static LockSingleton singleton;
private LockSingleton(){}
//详见:
public static LockSingleton getInstance(){
if(singleton==null){
synchronized(LockSingleton.class){
if(singleton==null){
singleton=new LockSingleton();
}
}
}
return singleton;
}
}
参考自:
关于java写一个单例模式和如何写一个单例模式的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
发布于:2022-12-06,除非注明,否则均为
原创文章,转载请注明出处。