随笔 - 312, 文章 - 14, 评论 - 1393, 引用 - 0

导航

<2009年3月>
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

公告

关注我的新浪微博

我的著作









常用链接

留言簿(126)

我参与的团队

随笔分类(818)

随笔档案(310)

文章分类(1)

文章档案(8)

相册

ADSL、3G查询

CSDN

eclipse

ibm

Java EE

Linux

Web

云服务

代理网站

关注的网站

协议

喜欢的Blog

国内广告平台

图书出版

在线培训

开发工具

微博客户端

手机铃声

操作系统

  • ReactOS
  • 一个与windowXP/2003兼容的操作系统

数学

文件格式

源码资源

移动(Mobile)

编程语言

英语学习

最新随笔

搜索

  •  

积分与排名

  • 积分 - 1969875
  • 排名 - 6

最新评论

阅读排行榜

评论排行榜

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):向线程传递数据的三种方法
     




Android开发完全讲义(第2版)(本书版权已输出到台湾)

http://product.dangdang.com/product.aspx?product_id=22741502



Android高薪之路:Android程序员面试宝典 http://book.360buy.com/10970314.html


新浪微博:http://t.sina.com.cn/androidguy   昵称:李宁_Lining

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

评论

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

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

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

volatile的原子操作是将读、写合二为一了,保证了其他线程读取变量时总是最新的,如果变量的值和自身以前的值相关,则volatile不起作用,如n++、n=n-1等。
2009-03-15 09:19 | 银河使者

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

@Leo1734
如果从bytecode角度看,Java源代码级的很多操作都不是原子的,javac将其编译成bytecode时都有多步组成。n = m也是由多步组成的,不过要给n加上volatile,n=m就是原子级的操作。
2009-03-15 09:21 | 银河使者

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

受教了,谢谢各位
2009-03-15 11:59 | 习习

# re: Java多线程初学者指南(6):慎重使用volatile关键字[未登录]  回复  更多评论   

volatile是保证变量对其他线程的可见性
2009-03-16 11:46 | jbahamut

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

其实我觉得这个问题应该是线程的问题,主要是线程执行的时间可能有两个同时获取这个int值,然后第一个线程增加后,第二个线程在增加,覆盖了其值,主要是线程没有对n 这个变量加锁,造成多个线程同时读取相同的值
2009-03-16 13:21 | guming123416

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

@guming123416
至于为什么volatile在某些时候不好使,这得问JVM了,可能是实现机制的问题,如果想保险点,应尽量少用volatile。thanking in java的作者也建议少用volatile。
2009-03-16 13:52 | 银河使者

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

你上面的我两个在JDK1.6上执行都是n=1000啊,似乎volatile有用,这还是原子操作哈
2009-03-19 19:21 | qiulijian

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

@qiulijian
你多运行几次,如运行20次再说,第一个例子不同步,第二个例子是同步的
2009-03-19 19:29 | 银河使者

# re: Java多线程初学者指南(6):慎重使用volatile关键字  回复  更多评论   

这里有自动拆封箱操作,n的对象一直在变化。
2015-08-26 16:40 | 皮鞋铮亮

只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问