Lazy<T>的使用方式(可实现单例模式)

简介:

Lazy<T>是延迟对象,通过它可以延迟创建T对象,也就是说,虽然已经声明了对象,但是实际上只有在使用T对象的时候,才会去创建这个对象。如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  MyClass
     {
         public  MyClass()
         {
             Console.WriteLine( "MyClass对象创建,其标识:{0}" this .GetHashCode());
         }
     }
  static  void  Main( string [] args)
         {
             Console.WriteLine(DateTime.Now);
             Lazy<MyClass> lazyObj =  new  Lazy<MyClass>();
             Thread.Sleep(2000); //等待2秒钟
             Console.WriteLine(DateTime.Now);
             Console.WriteLine( "MyClass对象的HashCode={0}" , lazyObj.Value.GetHashCode());
             Console.WriteLine( "MyClass对象的HashCode={0}" , lazyObj.Value.GetHashCode());
             Console.WriteLine( "MyClass对象的HashCode={0}" , lazyObj.Value.GetHashCode());
             Console.WriteLine( "MyClass对象的HashCode={0}" , lazyObj.Value.GetHashCode());
          }

得到结果为:

EF005F7B7BC54B3D813297D61E6B00B9

可以看到,虽然在25秒的时候,声明了 Lazy<MyClass>对象,但是MyClass对象的构造函数并没有马上调用。

程序延迟2秒后,打印lazyObj.Value中的MyClass对象的HashCode的时候,程序发现MyClass对象还未创建,才去创建,调用MyClass的构造函数。一旦创建完毕后,之后再次调用lazyObj.Value中的MyClass对象,也不会再去创建该对象。

在单线程的环境下,使用Lazy的意义也不是很明显。

在多线程环境下:

创建Lazy对象的时候,使用它的重载构造函数之一:

public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode);

参数valueFactory是一个委托,通过他来创建T对象。mode则是线程安全性模式。是一个枚举值,有三种值:

None

PublicationOnly

ExecutionAndPublication

如下一段测试代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  Program
     {
         static  void  Main( string [] args)
         {
              Lazy<MyClass> lazyObj =  new  Lazy<MyClass>(() => {  return  new  MyClass(); }, LazyThreadSafetyMode.None); //此处修改mode的参数(None,PublicationOnly,ExecutionAndPublication),看对程序有何影响
             for  ( int  i = 0; i < 10; i++)
             {
                 Thread th =  new  Thread(() => { Console.WriteLine( "线程调用对象{0}" , lazyObj.Value.GetHashCode()); });
                 th.Start();
             }
         }
     }
     class  MyClass
     {
         public  MyClass()
         {
             Console.WriteLine( "MyClass对象创建,其标识:{0}" this .GetHashCode());
         }
     }

当Mode参数为LazyThreadSafetyMode.None时,得到的结果如下:

A8CFC988730343C3A1067BF987891228

这错误原因是没有创建MyClass实例前,就有线程去访问对象的GetHashCode()方法了。这也说明了如果选用LazyThreadSafetyMode.None,那就不保证线程在访问Lazy对象的lazyObj.Value前先创建对象。这种情况下,当有些线程在启动前,很幸运的,该对象已经创建了,有些线程启动前则很不幸,对象并没有创建。因此会出现上面的那种异常。

当Mode参数为LazyThreadSafetyMode.PublicationOnly时,得到的结果如下:

358FD61145A54E71995E9B47B4E8FA64

可能每次运行的结果不同,但这次运行中可以看到,MyClass的构造函数被运行了3次,但是即使有3个对象创建了,在每次调用该对象时,只使用其中的一个对象。(不确定使用的是最先生成的那个对象,因为测试的时候,发现有时候使用的对象是后生成的。不过根据本人理解应该是使用最先生成的对象,由于在并发过程中,最先生成的对象不一定最先打印出来)

当Mode参数为LazyThreadSafetyMode.ExecutionAndPublication时,得到的结果如下:

018B375076F9460792D9F11A6F7DF69A

使用LazyThreadSafetyMode.ExecutionAndPublication,保证了对象只被创建一次。基于这个特性,可以使用LazyThreadSafetyMode.ExecutionAndPublication方式,实现在多线程下的单例模式。但是要注意:此时Lazy对象仅保证多个线程访问的是同一个对象,但不保证多线程访问对象时候是否同步,因此,如果要确保多线程访问同步访问同一个对象,最好还是采取lock等方式。














本文转自cnn23711151CTO博客,原文链接: http://blog.51cto.com/cnn237111/1213187,如需转载请自行联系原作者


相关文章
|
7月前
|
C++
C++实现单例模式-多种方式比较
单例模式,面试中经常被问到,但是很多人只会最简单的单例模型,可能连多线程都没考虑到,本文章从最简单的单例,到认为是最佳的单例模式实现方式,单例模式没有什么知识点,直接上源码
104 0
|
6月前
|
设计模式 存储 Java
JavaSE——面向对象高级一(2/4)-饿汉式单例、懒汉式单例、代码块、static的注意事项
JavaSE——面向对象高级一(2/4)-饿汉式单例、懒汉式单例、代码块、static的注意事项
44 0
|
安全 Java 编译器
单例模式的4种实现方式
单例模式的4种实现方式
112 0
|
7月前
|
安全
在单例模式中使用Atomic类的优缺点是什么?
【2月更文挑战第5天】【2月更文挑战第13篇】在单例模式中使用Atomic类的优缺点是什么?
74 0
|
7月前
|
Java Spring 容器
面试题:在spring框架下面,Bean的属性lazy-init有什么作用,默认值是多少
面试题:在spring框架下面,Bean的属性lazy-init有什么作用,默认值是多少
73 0
|
SQL Java
解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题最简单方法——基于枚举类型的单例实现
解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题最简单方法——基于枚举类型的单例实现
170 0
解决单例模式中懒汉式不支持高并发,饿汉式不支持懒加载问题最简单方法——基于枚举类型的单例实现
|
SQL 设计模式 安全
6. 单例模式有几种写法?
6. 单例模式有几种写法?
111 0
6. 单例模式有几种写法?
|
缓存
单例模式(5种实现方式)
单例模式(5种实现方式)
131 0
|
安全 Java
单例模式 之内部类延迟加载,(多)线程安全
单例模式 之内部类延迟加载,(多)线程安全
266 0
单例模式 之内部类延迟加载,(多)线程安全
|
Java Spring
@Lazy注解简单理解
@Lazy注解简单理解
347 0