`
TonyLian
  • 浏览: 396593 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【第48条】对共享可变数据的同步访问

阅读更多

《第9章 线 程》

 

    通过使用线程(thread)可以在同一程序中同时进行多个活动。多线程程序设计比单线程程序设计要困难得多。所以如果一个类库可以帮助你从低层的多线程程序设计中解脱出来,那么一定要使用这个类。即使这样,有时候仍然要编写或者维护多线程代码,所以本章包含的建议可以帮助我们写出清晰、正确、文档组织良好的多线程程序。

 

【第48条】对共享可变数据的同步访问

 

<我的废话:>

    如今我们喜爱Java、使用Java,很大情况下是由于Java的稳定、强大的表现,丰富的类库资源和出色的跨平台性。这些优势在使用Java作为B/S结构编程中的S的实现时都表现得淋漓尽致。然而,当我们广泛采用Java语言作为服务器端开发语言时,由于B/S系统的特点,我们可能很少关系多线程编程了。

 

    B/S的并发访问,本身就是多线程的,它往往已经由Servlet容器或EJB容器来完成了,我们所需要编写的“服务”只需要关心如何去处理一个客户端的访问即可。在这种情形下,多线程编程往往就被遗忘了。尤其是在企业应用开发和网站开发中,对某一客户的一次服务请求时,大多数情况下是用不着多线程的(业务逻辑没有复杂到那个份上)。

 

    然而,对于那些静态属性和单例模式的对象来说,就必须考略到他们的同步了。就是那些用于在不同客户间传递信息的单例对象和用于记录整个系统(网站)的某些状态的静态属性。

 

    言归正传,synchronized关键字可以保证在同一时刻,只有一个线程在执行被它“保护”起来的代码块。当一个线程在修改一个对象时,其他线程都不会看到对象处于不一致的状态中。或者说,当一个线程在同步代码块中修改一个对象时,其他线程也进入这个代码块时,看到的对象的状态仍然是第一线程修改前的状态;当第一个线程结束修改离开同步代码块后,其他线程再看此对象就是修改后的状态了。比较类似于数据库修改前的select for update。

 

    为了在线程之间可靠地通信,以及为了互斥访问,同步是需要的。如果有“为了提高性能而避免使用同步”的建议,那么这样的建议是非常危险而错误的。举一个例子:

 

    一个网站中有一个计数器,以提示来访者是第几位访客。

private static int visitorCounter = 0;

public static int getVisitorCounter(){
    return ++visitorCounter;
}

 

   这里显然使用一个静态的属性来保存全站全局性的计数器。当一个访客访问时,通过静态的getVisitorCounter()方法返回是第几位来访者。这个方法,首先将计数器加一,然后将加一后的数值返回,然而就是这个++运算实际是先取得对象的状态,再修改对象的状态。那么当多线程并发访问时(多个访问者同时访问此网页),不加同步的方法就有可能产生这样的效果:

 

    线程1取得visitorCounter为888887,此时线程2也取得了visitorCounter为888887,然后线程1将它取得的数值加一得到888888并返回,之后线程也将它取得的数值加一得到888888并返回。结果就是两个不同的访问者都看到自己是第888888位来访者。如果这个网站在之前的宣传中承诺将为幸运的第888888位访客提供丰厚的奖品的话,那么他们就有麻烦了,可能有好几位网友都拿着截屏来领奖了(PS的不算)。

 

    那么这个该如何防止呢?答案就是给方法以同步保护:

private static int visitorCounter = 0;

public static synchronized int getVisitorCounter(){
    return ++visitorCounter;
}

 

   这样就避免了当一个线程正在读/写对象状态时,被另一个线程读到“不稳定”的状态。

 

   总而言之,无论何时,当多个线程共享可变数据的时候,每个读或者写数据的线程必须获得一把锁。

 

 

 

 

【Effective Java 学习笔记】系列连载专题请见:
http://tonylian.iteye.com/categories/64208

0
0
分享到:
评论

相关推荐

    分布式算法 作者:(美)Nancy A.Lynch 舒继武 李国东part1

     本书可作为高等院校计算机系研究生的教材,尤其适合对计算机理论或体系结构感兴趣的学生学习,还适合分布式设计人员、研究人员及其相关技术人员参考。 出版者的话 专家指导委员会 译者序 前言 第1章 引言 1 1.1 ...

    Visual C#2010 从入门到精通(Visual.C#.2010.Step.By.Step).完整去密码锁定版 I部分

    18.5 可变性和泛型接口 340 18.5.1 协变接口 341 18.5.2 逆变接口 343 第18章快速参考 345 第19章 枚举集合 347 19.1 枚举集合中的元素 347 19.1.1 手动实现枚举器 348 19.1.2 实现ienumerable接口 352 19.2...

    C#微软培训资料

    第十八章 高 级 话 题 .235 18.1 注册表编程 .235 18.2 在 C #代码中调用 C++和 VB 编写的组件 .240 18.3 版 本 控 制 .249 18.4 代 码 优 化 .252 18.5 小 结 .254 第五部分 附 录 .255 附录 A 关 键 ...

    Java并发编程实战

    第四部分 高级主题 第13章 显式锁227 13.1 Lock与 ReentrantLock227 13.1.1 轮询锁与定时锁228 13.1.2 可中断的锁获取操作230 13.1.3 非块结构的加锁231 13.2 性能考虑因素231 13.3 公平性232 13.4 在...

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

     数据查询语言 (Data Query Language, DQL) 是SQL语言中,负责进行数据查询而不会对数据本身进行修改的语句,这是最基本的SQL语句。例如:SELECT(查询)  数据控制语言Data Controlling Language(DCL),用来...

    超级有影响力霸气的Java面试题大全文档

     封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性: ...

    Delphi5开发人员指南

    9.11.2 访问DLL中的共享数据 259 9.12 引出DLL中的对象 261 9.13 总结 265 第10章 Delphi 5的打印 266 10.1 TPrinter对象 266 10.2 TPrinter.Canvas 267 10.3 简单打印 267 10.3.1 打印TMemo组件中的内容 267 10.3.2...

    java 面试题 总结

    封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性: 多...

    JAVA面试题最全集

    如果要按照键值保存或者访问数据,使用什么数据结构? 要掌握Collection相关的接口和类的使用 56.使用StringBuffer类与String类进行字符串连接时有何区别? 57.调用Thread类的destroy()方法有什么后果? 58.多...

    asp.net知识库

    asp.net 2.0-实现数据访问(1) ASP.NET 2.0 新特性 .NET 2.0里使用强类型数据创建多层应用 在MastPage中引用脚本资源 2.0正式版中callback的一些变化+使用示例(ASP.NET 2.0) asp.net 2.0 新特性 Visual Web ...

    net学习笔记及其他代码应用

    数据访问层对数据库进行增删查改。 业务层一般分为二层,业务表观层实现与表示层的沟通,业务规则层实现用户密码的安全等。 表示层为了与用户交互例如用户添加表单。 优点: 分工明确,条理清晰,易于调试,而且...

    应用密码学,全文,doc被压缩无密码RAR

    第十八章 单向hash函数 344 18.1 背景 344 18.2 SNEFRU 346 18.3 N- hash 346 18.4 MD4 349 18.5 MD5 350 18.6 MD2 354 18.7 安全hash算法(SHA) 354 18.8 RIPE- MD 357 18.9 HAVAL 357 18.10 其它单向hash函数 358...

Global site tag (gtag.js) - Google Analytics