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,如需转载请自行联系原作者


相关文章
|
SQL 前端开发 搜索推荐
【Element-UI】实现动态树、数据表格及分页效果
在现代软件开发中,动态树、数据表格以及分页效果成为了许多应用的核心需求。随着业务规模和复杂性的增加,我们往往需要展示大量的层级结构数据,并且实现交互性强且高效的操作。 动态树提供了一种组织结构清晰、可伸缩的展示方式,使用户可以方便地查看和操作树节点。数据表格则是以表格形式呈现数据,在其中用户可以进行排序、筛选、编辑等操作。 而分页效果则能够将大量数据分割成易于管理和浏览的一页或一页的内容。这三种功能的结合,不仅使得我们能够更好地处理庞大的数据集合,同时也使得用户能够快速定位所需信息。 本文将介绍如何使用现代前端技术实现动态树、数据表格及分页
|
敏捷开发 人工智能 前端开发
让你爽到飞起的【懒人插件AutoScssStruct4Vue】VSCode根据template的标签目录自动一键生成CSS/SCSS/LESS结构,敏捷开发必备插件!!!
让你爽到飞起的【懒人插件AutoScssStruct4Vue】VSCode根据template的标签目录自动一键生成CSS/SCSS/LESS结构,敏捷开发必备插件!!!
|
SQL Cloud Native 架构师
深入浅出Presto:大数据查询引擎的原理与应用
【4月更文挑战第7天】Presto是高性能的分布式SQL查询引擎,专为大规模数据交互式分析设计。它采用分离式架构,内存计算和动态规划优化查询,支持跨源查询、交互式查询和ANSI SQL兼容性。应用于大数据分析、实时数据湖查询和云原生部署。Presto的灵活性和效率使其在大数据处理领域备受推崇,适合分析师、数据科学家和IT架构师使用。未来将在博客中分享更多实践和案例。
1516 1
|
存储 算法 数据挖掘
python5种算法模拟螺旋、分层填充、递归、迭代、分治实现螺旋矩阵ll【力扣题59】
python5种算法模拟螺旋、分层填充、递归、迭代、分治实现螺旋矩阵ll【力扣题59】
|
Java
深入理解 Java 8 函数式接口:定义、用法与示例详解
深入理解 Java 8 函数式接口:定义、用法与示例详解
721 2
|
Java Linux Maven
解决Java中的跨平台开发与部署问题
解决Java中的跨平台开发与部署问题
|
存储 缓存 安全
深入理解内存映射:mmap映射的背后原理以及和共享内存的差异
深入理解内存映射:mmap映射的背后原理以及和共享内存的差异
4633 0
|
Kubernetes 监控 调度
Kubernetes(K8s)与虚拟GPU(vGPU)协同:实现GPU资源的高效管理与利用
本文探讨了如何使用Kubernetes和虚拟GPU(vGPU)实现异构GPU的协同调度。Kubernetes是一个容器编排平台,通过设备插件、资源规格、调度器扩展和节点标签实现GPU资源管理。vGPU技术允许物理GPU资源在多个虚拟机或容器中共享。文章详细介绍了vGPU的部署配置步骤,并提出了GPU资源调度、负载均衡和监控调优的方法。强调虚拟GPU的性能取决于硬件和驱动支持,合理配置能提供高性能计算环境。参考文献包括Kubernetes和NVIDIA官方文档及相关研究论文。
|
JavaScript 前端开发 IDE
【TypeScript技术专栏】TypeScript与Node.js后端开发
【4月更文挑战第30天】TypeScript在Node.js后端开发中日益重要,作为JavaScript超集,它提供静态类型检查和面向对象编程,增强代码可靠性和维护性。集成TypeScript能带来类型安全、更好的IDE体验、易于维护的代码以及增强工具支持。通过安装TypeScript编译器、编写TypeScript文件、配置TSconfig,开发者可以在Node.js项目中利用其高级特性,提高代码质量和开发效率。实践案例显示,TypeScript能确保路由处理器的类型正确,降低错误率。随着社区发展,TypeScript成为提升Node.js开发体验的推荐选择。
623 0
|
存储 Ubuntu
Ubuntu查看系统日志的几种方法
Ubuntu查看系统日志的几种方法