- 浏览: 495275 次
- 性别:
- 来自: 苏州
文章分类
- 全部博客 (211)
- Flex (4)
- Java (22)
- Servlet (4)
- 学习心得 (2)
- 生活琐事 (3)
- PHP (0)
- JavaScript (4)
- Linux (3)
- MYSQL (0)
- SQL SERVER (2)
- ORACLE (5)
- 项目技术积累 (0)
- 设计模式 (0)
- Photoshop (0)
- 网页制作 (8)
- 值得记住 (3)
- Struts1.x (7)
- Hibernate (31)
- IDE (10)
- Spring (5)
- EXT (0)
- Junit (4)
- dom4j (2)
- Log4j (3)
- Java标注 (1)
- WebService (1)
- JSON (2)
- Struts2,x (19)
- Ajax (6)
- 英文 (1)
最新评论
-
aduo_vip:
支持博主,好文!正好需要了
java怎样读写和修改XML文件?? -
默默pig:
楼主,您好。有个语法想问一下:引用你原文中“<resul ...
Struts2中redirect基本的经验之谈 -
Andrew0721:
road_16 写道楼主你好,你说到
redirect:act ...
Struts2中redirect基本的经验之谈 -
zhengkunsheng:
Struts2中redirect基本的经验之谈 -
road_16:
楼主你好,你说到
redirect:action处理完后重定向 ...
Struts2中redirect基本的经验之谈
Java中的变量分为两类:局部变量和类变量。局部变量是指在方法内定义的变量,如在run方法中定义的变量。对于这些变量来说,并不存在线程之间共享的问题。因此,它们不需要进行数据同步。类变量是在类中定义的变量,作用域是整个类。这类变量可以被多个线程共享。因此,我们需要对这类变量进行数据同步。
数据同步就是指在同一时间,只能由一个线程来访问被同步的类变量,当前线程访问完这些变量后,其他线程才能继续访问。这里说的访问是指有写操作的访问,如果所有访问类变量的线程都是读操作,一般是不需要数据同步的。
那么如果不对共享的类变量进行数据同步,会发生什么情况呢?让我们先看看下面的代码会发生什么样的事情:
public class MyThread extends Thread
{
public static int n = 0;
public void run()
{
int m = n;
yield();
m++;
n = m;
}
public static void main(String[] args) throws Exception
{
MyThread myThread = new MyThread ();
Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
threads[i] = new Thread(myThread);
for (int i = 0; i < threads.length; i++)
threads[i].start();
for (int i = 0; i < threads.length; i++)
threads[i].join();
System.out.println("n = " + MyThread.n);
}
}
在执行上面代码的可能结果如下:
看到这个结果,可能很多读者会感到奇怪。这个程序明明是启动了100个线程,然后每个线程将静态变量n加1。最后使用join方法使这100个线程都运行完后,再输出这个n值。按正常来讲,结果应该是n = 100。可偏偏结果小于100。
其实产生这种结果的罪魁祸首就是我们经常提到的“脏数据”。而run方法中的yield()语句就是产生“脏数据”的始作俑者(不加yield语句也可能会产生“脏数据”,但不会这么明显,只有将100改成更大的数,才会经常产生“脏数据”,在本例中调用yield就是为了放大“脏数据”的效果)。yield方法的作用是使线程暂停,也就是使调用yield方法的线程暂时放弃CPU资源,使CPU有机会来执行其他的线程。为了说明这个程序如何产生“脏数据”,我们假设只创建了两个线程:thread1和thread2。由于先调用了thread1的start方法,因此,thread1的run方法一般会先运行。当thread1的run方法运行到第一行(int m = n;)时,将n的值赋给m。当执行到第二行的yield方法后,thread1就会暂时停止执行,而当thread1暂停时,thread2获得了CPU资源后开始运行(之前thread2一直处于就绪状态),当thread2执行到第一行(int m = n;)时,由于thread1在执行到yield时n仍然是0,因此,thread2中的m获得的值也是0。这样就造成了thread1和thread2的m获得的都是0。在它们执行完yield方法后,都是从0开始加1,因此,无论谁先执行完,最后n的值都是1,只是这个n被thread1和thread2各赋了一遍值。这个过程如下图如示:
也许有人会问,如果只有n++,会产生“脏数据”吗?答案是肯定的。那么n++只是一条语句,又如何在执行过程中将CPU交给其他的线程呢?其实这只是表面现象,n++在被Java编译器编译成中间语言(也叫做字节码)后,并不是一条语言。让我们看看下面的Java代码将会被编译成什么样的Java中间语言。
Java源代码
{
n++;
}
被编译后的中间语言代码
002 {
003 aload_0
004 dup
005 getfield
006 iconst_1
007 iadd
008 putfield
009 return
010 }
大家可以看到在run方法中只有n++一条语句,而在编译后,却有7条中间语言语句。我们并不需要知道这些语句的功能是什么,只看一下第005、007和008行语句。在005行是getfield,根据它的英文含义可知是要得到某个值,因为这里只有一个n,所以毫无疑问,是要得到n的值。而在007行的iadd也不难猜测是将这个得到的n值加1。在008行的putfield的含义我想大家可能已经猜出来了,它负责将这个加1后的n再更新回类变量n。说到这,可能大家还有一个疑惑,执行n++时直接将n加1不就行了,为什么要如此费周折。其实这里涉及到一个Java内存模型的问题。
Java的内存模型分为主存储区和工作存储区。主存储区保存了Java中所有的实例。也就是说,在我们使用new来建立一个对象后,这个对象及它内部的方法、变量等都保存在这一区域,在MyThread类中的n就保存在这个区域。主存储区可以被所有线程共享。而工作存储区就是我们前面所讲的线程栈,在这个区域里保存了在run方法以及run方法所调用的方法中定义的变量,也就是方法变量。在线程要修改主存储区中的变量时,并不是直接修改这些变量,而是将它们先复制到当前线程的工作存储区,在修改完后,再将这个变量值覆盖主存储区的相应的变量值。
在了解了
Java
的内存模型后,就不难理解为什么
n++
也不是原子操作了。它必须经过一个拷贝、加
1
和覆盖的过程。这个过程和在MyThread类中模拟的过程类似。大家可以想象,如果在执行到
getfield
时,
thread1
由于某种原因被中断,那么就会发生和MyThread类的执行结果类似的情况。要想彻底解决这个问题,就必须使用某种方法对n进行同步,也就是在同一时间只能有一个线程操作n,这也称为对n的原子操作。
发表评论
-
浅析Java抽象类和接口的比较
2012-10-23 14:26 1114抽象类(abstract class) ... -
java序列化(Serializable)
2009-10-15 00:05 1498序列化机制只保存对象的类型信息,属性的类型信息和属性值,和方法 ... -
Java Serializable(序列化)的理解和总结
2009-10-13 09:05 11231、序列化是干什么的? 简单说就是为了保存在内存中 ... -
java怎样读写和修改XML文件??
2009-10-10 16:37 15058xml: <?xml version="1. ... -
Java时间格式转换大全
2009-10-10 13:39 1475import java.text.*; import jav ... -
JAVA提高教程(6)-认识List列表
2009-10-10 00:34 1338列表是很常用的数据结构,感觉比Map和Set用的频率要高一些吧 ... -
Java 中 Vector、ArrayList、List 使用深入剖析
2009-10-10 00:29 807线性表,链表,哈希表 ... -
JAVA LIST 遍历
2009-10-09 17:02 2749对List的遍历有三种方式 List< ... -
读取Properties文件的几种方法
2009-09-08 14:08 9351. 使用java.util.Properties类的load ... -
java正则表达式
2009-09-08 08:52 1130Java正则表达式学习: ... -
如何打包jar
2009-09-07 14:32 1651jar cvf my.jar helloword.class将 ... -
(JAVA)IP地址相关的工具类 IPUtil
2009-09-07 11:21 3246import java.net.*; /** * IP ... -
java反射应用对BEAN的操作,写通用类、标签时有用
2009-09-07 11:17 1550import java.lang.reflect.Field; ... -
Java字符串通用类
2009-09-07 11:15 1984import java.util.regex.Matcher; ... -
一个关于日期,数字,字符格式化的常用工具类
2009-09-07 11:13 1833import java.text.SimpleDateForm ... -
Java 通用异常处理类
2009-09-07 10:32 1400/** * (#)ThrowableManager.jav ... -
优化的 JAVA 数据库连接通用类
2009-09-07 10:14 3850之前我曾经放出过一个通用的数据库连接DAO。但是其也有一定的局 ... -
验证码的java通用类
2009-09-07 10:09 996在应用程序中为防止系 ... -
java实现发送电子邮件功能
2009-09-07 09:14 1518在开发中经常要给用户自动的发送电子邮件,今天在网上搜了一个发送 ... -
高频词汇提取的Java实现
2009-09-07 08:53 1479面对浩瀚的信息海洋,找到想要的资源有时真的是不容易。在大量文字 ...
相关推荐
本文将深入探讨Java多线程中的线程生命周期,以及如何控制线程的状态转换。 线程的生命周期通常包括五个基本状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和终止(Terminated)。下面...
Java多线程初学者指南是一份详尽的教育资源,涵盖了多线程编程的基本概念和实践技巧,适合初学者深入理解这一重要技术。本指南通过12篇文档,逐步引导学习者掌握Java多线程的核心知识。 首先,让我们从基础开始,...
Java 多线程初学者指南之向线程传递数据的三种方法 在多线程编程中,向线程传递数据是一项非常重要的任务。与传统的同步开发模式不同,在多线程异步开发模式下,数据的传递和返回方式有很大的区别。因此,本文将...
内容概要:本文详细介绍了Java多线程的基本概念、实现方式、线程控制方法、线程同步、线程池及其应用。首先解释了线程的概念及其优势,接着讲述了如何通过继承Thread类、实现Runnable接口和使用Callable接口来创建多...
内容概要:本文详细介绍了Java多线程的基础...其他说明:本文通过实际案例和代码示例,深入浅出地讲解了Java多线程的核心知识点,有助于初学者快速入门并进阶。建议在学习过程中亲自编写并调试代码,以加深理解和记忆。
本文将深入探讨Java多线程模型的相关知识点,包括线程与进程的区别、线程的实现原理、线程的创建方法以及线程的阻塞与唤醒机制等,旨在为初学者提供一个清晰的多线程概念理解和使用指南。 一、线程与进程的区别 在...
《多线程编程指南》是一本深入探讨多线程编程技术的专业书籍,旨在帮助开发者理解和掌握如何在并发...无论你是初学者还是经验丰富的开发者,这本书都会提供有价值的洞见和技巧,帮助你构建高效、可靠的多线程应用程序。
总之,《Java多线程设计模式详解》是一本全面且实用的指南,无论你是Java初学者还是经验丰富的开发者,都能从中受益,提升你的多线程编程技巧。通过学习这本书,你可以更好地驾驭Java的多线程世界,编写出高效、稳定...
本资源包针对初学者提供了全面的多线程学习资料,旨在帮助你快速入门并解决实际开发中的问题。 首先,线程是操作系统分配CPU时间的基本单元,它允许程序同时执行多个任务。多线程编程能够提高程序的并发性和执行...
《Java多线程编程》第三版是一本深入探讨Java并发编程的权威指南,旨在帮助开发者理解和掌握Java平台上的多线程技术。这本书详尽地介绍了如何有效地利用多核处理器的性能,以及如何在复杂的并发环境中设计和实现高效...
这篇总结将深入探讨Java多线程的基础概念、特性以及常见用法,旨在为初学者提供一个全面的学习指南。 一、线程的基本概念 在Java中,线程是程序执行的最小单位,每个线程都有自己的程序计数器、虚拟机栈、本地方法...
总之,"java初学者的PPT"是一份全面的入门指南,覆盖了从基础到进阶的Java知识,对于想要踏入编程世界的初学者来说,无疑是一份宝贵的资源。通过深入学习和实践,你将能够逐步掌握Java编程,为未来的开发工作打下...
"java初学者教程初学者下载看看"这个标题暗示了这是一个专门为Java新手设计的学习资源,旨在帮助他们从零开始了解和学习Java编程。 "Java初学者教程:使用初学者,从不懂倒懂健康成长"的描述,表达了这个教程的目标...
《Java学习指南第四版下册》是一本专为Java初学者和进阶者设计的教程,旨在帮助读者全面深入地掌握Java编程语言的核心概念和技术。该书中文版的第四版下册涵盖了Java语言的高级特性,是理解Java编程精髓的重要资料。...
【JAVA 数据同步软件Sync源代码】是一个专为JAVA初学者设计的本地数据同步工具,它模仿了云盘同步的功能,但并不依赖网络环境,而是通过本地文件系统的操作实现数据的同步与备份。这个项目对于想要深入理解JAVA编程...
本指南将深入探讨Java线程的基本概念、创建方式以及管理策略,旨在帮助初学者快速掌握线程的工作原理。 一、Java线程基础知识 在Java中,线程是程序中的单一顺序控制流程。一个进程可以包含多个线程,每个线程都有...
本资料“java-Thread-study-summary.zip”提供了一个深入学习Java多线程的综合指南,特别适合初学者进行实践操作。 1. **线程的创建** - 继承`Thread`类:创建一个新的类,该类继承自`Thread`,然后重写`run()`...
《Java学习指南上册》是一本专为Java初学者和进阶者设计的教程书籍,旨在帮助读者深入理解和掌握Java编程语言。这份压缩包包含了该书的上下两部分,分别为"Java学习指南第四版上册part1.pdf"和"Java学习指南第四版...