volatile关键字相信了解Java多线程的读者都很清楚它的作用,和sychnorized 一样用于多线程的同步。volatile关键字用于声明简单类型变量,如int、float、boolean等数据类型(synchronized 用于方法或者代码段)。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。但这有一定的限制。例如,下面的例子中的n就不是原子级别的:

 package mythread;

 public class JoinThread extends Thread
{
public static volatile int n = 0;
public void run()
{
for (int i = 0; i < 10; i++)
try
{
n = n + 1;
sleep(3); // 为了使运行结果更随机,延迟3毫秒 }
catch (Exception e)
{
}
} public static void main(String[] args) throws Exception
{ Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
// 建立100个线程
threads[i] = new JoinThread();
for (int i = 0; i < threads.length; i++)
// 运行刚才建立的100个线程
threads[i].start();
for (int i = 0; i < threads.length; i++)
// 100个线程都执行完后继续
threads[i].join();
System.out.println("n=" + JoinThread.n);
}
}

如果对n的操作是原子级别的,最后输出的结果应该为n=1000,而在执行上面积代码时,很多时侯输出的n都小于1000,这说明n=n+1不是原子级别的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:

n = n + 1;

n++;

如果要想使这种情况变成原子操作,需要使用synchronized关键字,如上的代码可以改成如下的形式:

 package mythread;

 public class JoinThread extends Thread
{
public static int n = 0; public static synchronized void inc()
8 {
9 n++;
10 }
public void run()
{
for (int i = 0; i < 10; i++)
try
{
inc(); // n = n + 1 改成了 inc();
sleep(3); // 为了使运行结果更随机,延迟3毫秒 }
catch (Exception e)
{
}
} public static void main(String[] args) throws Exception
{ Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
// 建立100个线程
threads[i] = new JoinThread();
for (int i = 0; i < threads.length; i++)
// 运行刚才建立的100个线程
threads[i].start();
for (int i = 0; i < threads.length; i++)
// 100个线程都执行完后继续
threads[i].join();
System.out.println("n=" + JoinThread.n);
}
}

上面的代码将n=n+1改成了inc(),其中inc方法使用了synchronized关键字进行方法同步。因此,在使用volatile关键字时要慎重,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。

Java多线程初学者指南(6):慎重使用volatile关键字的更多相关文章

  1. Java多线程初学者指南系列教程

    转自:http://developer.51cto.com/art/200911/162925.htm 51cto 本系列来自NokiaGuy的“真的有外星人吗”博客,系列名称为<Java多线程 ...

  2. 【系列】Java多线程初学者指南(1):线程简介

    原文地址:http://www.blogjava.net/nokiaguy/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/19/26075 ...

  3. Java多线程干货系列—(四)volatile关键字

    原文地址:http://tengj.top/2016/05/06/threadvolatile4/ <h1 id="前言"><a href="#前言&q ...

  4. Java多线程初学者指南(7):向线程传递数据的三种方法

    在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果.但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别.由于线程 ...

  5. Java多线程初学者指南(4):线程的生命周期

    与人有生老病死一样,线程也同样要经历开始(等待).运行.挂起和停止四种不同的状态.这四种状态都可以通过Thread类中的方法进行控制.下面给出了Thread类中和这四种状态相关的方法. // 开始线程 ...

  6. Java多线程初学者指南(12):使用Synchronized块同步变量

    我们可以通过synchronized块来同步特定的静态或非静态方法.要想实现这种需求必须为这些特性的方法定义一个类变量,然后将这些方法的代码用synchronized块括起来,并将这个类变量作为参数传 ...

  7. Java多线程初学者指南(10):使用Synchronized关键字同步类方法

    要想解决“脏数据”的问题,最简单的方法就是使用synchronized关键字来使run方法同步,代码如下: public synchronized void run() { ... } 从上面的代码可 ...

  8. Java多线程初学者指南(9):为什么要进行数据同步

    Java中的变量分为两类:局部变量和类变量.局部变量是指在方法内定义的变量,如在run方法中定义的变量.对于这些变量来说,并不存在线程之间共享的问题.因此,它们不需要进行数据同步.类变量是在类中定义的 ...

  9. Java多线程初学者指南(8):从线程返回数据的两种方法

    从线程中返回数据和向线程传递数据类似.也可以通过类成员以及回调函数来返回数据.但类成员在返回数据和传递数据时有一些区别,下面让我们来看看它们区别在哪. 一.通过类变量和方法返回数据 使用这种方法返回数 ...

随机推荐

  1. cocos2dx下最大纹理大小取决于平台

    原文:http://www.cocos2d-x.org/wiki/Max_size_of_textures_in_cocos2d-x_depends_on_each_platform 在理论上,coc ...

  2. Ubuntu上架设PPPoE Server

    一.安裝 PPPoE Server Software1)sudo apt-get install ppp2)rp-pppoe(非apt套件)wget -c http://www.roaringpeng ...

  3. TextView使用的方式

    第一种改变TextView的方式 //      TextView tv=new TextView(this);  //      tv.setText("Hello,Android&quo ...

  4. Android 开发第二天

    开发入门HelloWorld 首先打开开发工具 第一步 第二步 效果图 以后可以点击一直下去 第三步骤介绍一下里面项目的作用 SRC是用来保存源代码的东西MainAcrivity.java主视图res ...

  5. Mac安装Mysql无法登录

    Mac安装的Mysql5.7.10 执行mysql -u root -p 就要求输入密码,但是新安装是没密码的.然后就会报错. ERROR 1045 (28000): Access denied fo ...

  6. (转)Excel导出及数据格式化处理

    原文地址:http://www.cnblogs.com/cnaspnet/archive/2008/05/24/1206263.html public void ToExcel(System.Web. ...

  7. WildFly 9.0.2 + SQLServer + BIRT + JNDI 处理BIRT报表发布时数据源的修改

    一.前提需求说明: 最近在做项目的时候使用jboss WildFly发布BIRT报表,在报表中我是直接添加的JDBC数据源,通过Database URL .用户名和密码连接数据库.后来在部署到正式和测 ...

  8. c#调用c++ dll(一)

    首先来说说c++中的dll 核心的一些知识 比较大的应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作.可能存在一些模块的功能较为通用,在构造其它软件系统时 ...

  9. cognos 10.2.2 report studio数字---字符型查询注意事项

    做了一个简单的报表,就是按照员工编号查询员工,其中员工编号是全数字,我们保存在数据库中的是字符型varchar2(10),所以在report studio中做查询就一直报告服务器错误. 其中使用cas ...

  10. VC中动态添加控件

    VC中动态添加控件 动态控件是指在需要时由Create()创建的控件,这与预先在对话框中放置的控件是不同的. 一.创建动态控件: 为了对照,我们先来看一下静态控件的创建. 放置静态控件时必须先建立一个 ...