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

本文为原创,如需转载,请注明作者和出处,谢谢!

上一篇:Java多线程初学者指南(5):join方法的使用

    volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量,如int、float、boolean等数据类型。如果这些简单数据类型声明为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 + 1;
n
++;

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

public class JoinThread extends Thread
{
    
public static int n = 0;

    
public static synchronized void inc()
    {
        n
++;
    }
    
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多线程初学者指南(7):向线程传递数据的三种方法
     


国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

posted on 2009-03-14 16:44 银河使者 阅读(2366) 评论(9)  编辑  收藏 所属分类: java 原创多线程

评论

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-15 09:00 Leo1734

使用volatile关键字时该变量必须独立于程序的其他内容和这个变量以前的值。
btw:n++的动作实际由“读,添加,写”三个步骤组成的,并不是原子操作。
  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-15 09:19 银河使者

volatile的原子操作是将读、写合二为一了,保证了其他线程读取变量时总是最新的,如果变量的值和自身以前的值相关,则volatile不起作用,如n++、n=n-1等。  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-15 09:21 银河使者

@Leo1734
如果从bytecode角度看,Java源代码级的很多操作都不是原子的,javac将其编译成bytecode时都有多步组成。n = m也是由多步组成的,不过要给n加上volatile,n=m就是原子级的操作。
  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-15 11:59 习习

受教了,谢谢各位  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字[未登录] 2009-03-16 11:46 jbahamut

volatile是保证变量对其他线程的可见性  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-16 13:21 guming123416

其实我觉得这个问题应该是线程的问题,主要是线程执行的时间可能有两个同时获取这个int值,然后第一个线程增加后,第二个线程在增加,覆盖了其值,主要是线程没有对n 这个变量加锁,造成多个线程同时读取相同的值  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-16 13:52 银河使者

@guming123416
至于为什么volatile在某些时候不好使,这得问JVM了,可能是实现机制的问题,如果想保险点,应尽量少用volatile。thanking in java的作者也建议少用volatile。  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-19 19:21 qiulijian

你上面的我两个在JDK1.6上执行都是n=1000啊,似乎volatile有用,这还是原子操作哈  回复  更多评论   

# re: Java多线程初学者指南(6):慎重使用volatile关键字 2009-03-19 19:29 银河使者

@qiulijian
你多运行几次,如运行20次再说,第一个例子不同步,第二个例子是同步的  回复  更多评论   


专题:Android  iPad  jQuery  Chrome OS

博客园首页  IT新闻  知识库  学英语  Java程序员招聘
标题  
姓名  
主页
验证码 *  
内容(请不要发表任何与政治相关的内容)  
  登录      
每天10分钟,轻松学英语


网站导航:
 
<2009年3月>
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

公告

我的其他blog

http://nokiaguy.cnblogs.com
http://blog.csdn.net/nokiaguy

我的著作





正在读的书




常用链接

留言簿(31)

我参与的团队

随笔分类(662)

随笔档案(255)

文章分类(1)

文章档案(3)

相册

ADSL、3G查询

CSDN

eclipse

ibm

Java EE

Linux

Web

云服务

代理网站

关注的网站

喜欢的Blog

图书出版

开发工具

手机铃声

操作系统

数学

文件格式

源码资源

移动(Mobile)

编程语言

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜