`
TonyLian
  • 浏览: 401594 次
  • 性别: 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
分享到:
评论

相关推荐

    计算机网络试题选择题精华

    - **曼彻斯特编码**:一种自含时钟编码方式,每个比特中间都有跳变,可用于同步。 - **基带传输**:在数字通信信道上,直接传输数字数据信号的方法。 #### 六、模拟数据编码方法 - **振幅键控(ASK)**:改变载波的...

    自考计算机网络原理练习题汇总.docx

    4. **专用线路通信**:第四题中,采用专用线路通信可以省去建立通信线路的阶段,因为专用线路始终连接并为特定用户预留。 5. **通信系统要素**:第五题指出通信系统的基本组成部分是信源、通信媒体和信宿,这涵盖了...

    文件服务器架设——七步走

    #### 五、第四步:设置共享文件夹 根据向导的指引,完成以下设置: - 输入共享文件夹的路径,可通过浏览按钮找到之前创建的文件夹。 - 确定共享名称,建议保持与文件夹名一致或具有明确的含义。 - 添加描述,方便...

    局域网组建技术第五章网络连接与主干网络技术资料.ppt

    而PTM将数据分成可变长度的数据单元,每个单元包含路由、流控制和纠错信息,适合处理突发数据流,采用统计时分复用。 ATM(Asynchronous Transfer Mode)异步传送模式是介于STM和PTM之间的一种技术,以53字节的定长...

    java设计 原则61条

    在第121页中指出,线程同步是处理多线程环境中资源共享的关键技术,应当合理处理线程间的同步问题,确保数据的一致性。 **原则57:合理处理锁的竞争** 同样在第122页中提到,锁的竞争可能导致程序性能下降,应当...

    谢希仁第七版 计算机网络.docx

    计算机网络知识点总结 计算机网络是指将分布在不同地理位置的计算机通过通信线路连接起来,实现资源共享、信息交换和通信协作的系统。以下是计算机网络相关知识点的总结: ...* 可选字段(长度可变) * 填充数据部分

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

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

    电大本科操作系统.doc

    可变分区存储根据作业实际需求划分。PV操作是低级通信,用于进程同步。Shell是UNIX中的命令解释程序。死锁处理策略包括预防、避免和检测恢复。最后,操作系统是系统软件,管理硬件和软件资源,其部分程序在系统态...

    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++并发编程实践 C++ Concurrency in Action

    第四章讨论了多种同步机制,这些机制可以帮助开发者更好地控制线程间的执行顺序,避免死锁和活锁等问题。 - **条件变量**:`std::condition_variable`允许线程等待某个条件满足后再继续执行。 - **信号量**:`std::...

    操作系统复习试题(卷)与答案解析.doc

    47. 可变分区分配可能导致碎片问题,影响存储空间的利用率。 48. 逻辑结构上,文件分为有结构文件和无结构文件。 49. SPOOLING技术通过模拟脱机输入输出实现并发I/O,提高系统效率。 50. 缓冲区的引入解决了I/O...

    操作系统复习试题及答案.doc

    47. 可变分区分配可能导致碎片问题。 48. 逻辑文件结构分为有结构文件和无结构文件。 49. SPOOLING技术通过模拟脱机输入输出实现I/O控制。 50. 引入缓冲可以解决慢速I/O设备与高速CPU之间的速度不匹配问题。 这些...

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

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

    java多线程详解

    除了使用内置的同步机制外,开发者还可以采用原子操作、不可变对象和线程局部变量等策略来避免数据竞争条件。 总之,Java多线程机制为开发者提供了强大的并发编程能力,但同时也带来了复杂性和潜在的挑战。理解并...

Global site tag (gtag.js) - Google Analytics