网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > Java
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
网络编程:ASP教程,ASP.NET教程,PHP教程,JSP教程,C#教程,数据库,XML教程,Ajax,Java,Perl,Shell,VB教程,Delphi,C/C++教程,软件工程,J2EE/J2ME,移动开发
本月文章推荐
.org.eclipse.cdt.sdk-3.0.0-win3.
.Java游戏编程分析--棋子的拖动.
.Java对象序列化(整理篇).
.java字符串的简单介绍.
.最大化JAVA代码的可重用性.
.错误的堆大小产生的“堆问题”.
.上海某全球五百强面试题(嵌入式.
.一个绝对害了不少人的Java技术问.
.concat 方法(String).
.使用EJB 组件的判断标准.
.Java极度性能调整.
.J2EE中使用Display标记库来展示表.
.有感“Java is not platform-ind.
.程序员:Waiting Anywhere.
.Java学习之神奇的i=i++.
.用J2SE 1.4 进行 Internet 安全编.
.使用SMTP协议发送邮件.
.toString 方法.
.开发框架--分析Tomcat下中文的彻.
.探索JDK1.5高级编码特征.

破除java神话之原子操作都是线程安全的

发表日期:2008-1-5



  Java中原子操作是线程安全的论调经常被提到。根据定义,原子操作是不会被打断地的操作,因此被认为是线程安全的。实际上有一些原子操作不一定是线程安全的。
  
  这个问题出现的原因是尽量减少在代码中同步要害字。同步会损害性能,虽然这个损失因JVM不同而不同。另外,在现代的JVM中,同步的性能正在逐步提高。尽管如此,使用同步仍然是有性能代价的,并且程序员永远会尽力提高他们的代码的效率,因此这个问题就延续了下来。
  
  在java中,32位或者更少位数的赋值是原子的。在一个32位的硬件平台上,除了double和long型的其它原始类型通常都是使用32位进行表示,而double和long通常使用64位表示。另外,对象引用使用本机指针实现,通常也是32位的。对这些32位的类型的操作是原子的。
  
  这些原始类型通常使用32位或者64位表示,这又引入了另一个小小的神话:原始类型的大小是由语言保证的。这是不对的。java语言保证的是原始类型的表数范围而非JVM中的存储大小。因此,int型总是有相同的表数范围。在一个JVM上可能使用32位实现,而在另一个JVM上可能是64位的。在此再次强调:在所有平台上被保证的是表数范围,32位以及更小的值的操作是原子的。
  
  那么,原子操作在什么情况下不是线程安全的?主要的一点是他们也许确实是线程安全的,但是这没有被保证!java线程答应线程在自己的内存区保存变量的副本。答应线程使用本地的私有拷贝进行工作而非每次都使用主存的值是为了提高性能。考虑下面的类:
  
  
  class RealTimeClock
  {
   private int clkID;
   public int clockID()
   {
   return clkID;
   }
   public void setClockID(int id)
   {
   clkID = id;
   }
  //...
  }
  
  现在考虑RealTimeClock的一个实例以及两个线程同时调用setClockID和clockID,并发生以下的事件序列:
  
  T1 调用setClockID(5)
  T1将5放入自己的私有工作内存
  T2调用setClockID(10)
  T2将10放入自己的私有工作内存
  T1调用clockID,它返回5
  5是从T1的私有工作内存返回的
  
  对clockI的调用应该返回10,因为这是被T2设置的,然而返回的是5,因为读写操作是对私有工作内存的而非主存。赋值操作当然是原子的,但是因为JVM答应这种行为,因此线程安全不是一定的,同时,JVM的这种行为也不是被保证的。
  
  两个线程拥有自己的私有拷贝而不和主存一致。假如这种行为出现,那么私有本机变量和主存一致必须在以下两个条件下:
  
  1、变量使用volatile声明
  2、被访问的变量处于同步方法或者同步块中
  
  假如变量被声明为volatile,在每次访问时都会和主存一致。这个一致性是由java语言保证的,并且是原子的,即使是64位的值。(注重很多JVM没有正确的实现volatile要害字。你可以在www.javasoft.com找到更多的信息。)另外,假如变量在同步方法或者同步块中被访问,当在方法或者块的入口处获得锁以及方法或者块退出时释放锁是变量被同步。
  使用任何一种方法都可以保证ClockID返回10,也就是正确的值。变量访问的频度不同则你的选择的性能不同。假如你更新很多变量,那么使用volatile可能比使用同步更慢。记住,假如变量被声明为volatile,那么在每次访问时都会和主存一致。与此对照,使用同步时,变量只在获得锁和释放锁的时候和主存一致。但是同步使得代码有较少的并发性。
  
  假如你更新很多变量并且不想有每次访问都和主存进行同步的损失或者你因为其它的原因想排除并发性时可以考虑使用同步。
上一篇:Java线程总结 人气:758
下一篇:java的线程安全四种方式五个等级 人气:1084
浏览全部Java的内容 Dreamweaver插件下载 网页广告代码 祝你圣诞节快乐 2009年新年快乐