extern、static、volatile 关键字

extern

extern的两个作用:

  • 修饰变量或函数,提示编译器此变量或函数是在其它文件中定义的,但要在此处引用;
  • 进行链接指定,如: extern "C" void fun(int a, int b);  告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,;

static

static的主要作用:

  • static局部变量,静态变量,函数返回时不会销毁;
  • static全局变量,仅在当前文件可见;
  • static函数,仅在当前文件可见;
  • 修饰类成员,静态成员为所有对象所有,不属于某个特定的对象;static成员函数没有this指针,只能访问类的static成员变量;

另外,static变量如果没有初始化,会自动用0填充;

volatile

volatile 变量是随时可能发生变化的,编译器不要对其进行优化,以免出错。

例如:

int i=10;
int j = i;
...
int k = i; 

由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中,而不是重新从i里面读。

又例如在嵌入式编程中,需要往某地址发送两条指令:

int *ip =...;     //设备地址
*ip = 1;     //第一个指令
*ip = 2;     //第二个指令 

以上代码可能会被编译器优化成:

int *ip = ...;
*ip = 2;

导致第一条指令丢失。

如果用volatile关键字声明变量,就不允许编译器做优化:

volatile int *ip =...;     //设备地址
*ip = 1;     //第一个指令
*ip = 2;     //第二个指令 

定义为 volatile 的变量是说该变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。准确地说就是,编译器在用到volatile变量时必须从内存读取,而不能使用保存在寄存器里的值。

下面是volatile变量的几个例子:

  1. 并行设备的硬件寄存器(如:状态寄存器);
  2. 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables);
  3. 多线程应用中的共享变量;

volatile在多线程环境中的应用,可以先看下面的例子:

volatile long int n = 0;

void foo()
{
    int i;
    for(i=0; i<10000; i++) {
        n++;
    }
}

#define N 100

int main()
{
    int k;
    pthread_t th[N];

    for (k=0; k<N; k++) {
        pthread_create(&th[k], NULL, (void*)foo, NULL);
    }   

    for (k=0; k<N; k++) {
        pthread_join(th[k], NULL);
    }   

    printf("n=%ld\n", n);
    return 0;
}

并发100个线程,每个线程对共享变量n累加1万次,理论上最后的结果应该是100w,但代码实际结果却是比这个更小的一个随机值。

我们知道volatile修饰的变量在每次被线程访问时,都强迫从内存中重读该变量的值。而且,当变量值发生变化时,强迫线程将变化值回写到内存。这样在任何时刻,两个不同的线程总是看到某个变量的同一个值。

既然如此,为何每次结果还是不同呢,这是因为n++操作并非原子性,也就是说volatile不能保证所修饰的变量进行原子操作。

以汇编过程来看自增操作:

mov        eax,dword ptr [ebp-8]     ;把i的值mov到eax寄存器
add        eax,1                     ;自加
mov        dword ptr [ebp-8],eax     ;再存放至i变量中 

分为3个步骤:

1)读取volatile变量值到寄存器;

2)增加寄存器的值;

3)把寄存器的值回写内存;

可见,如果线程A在步骤2、3之间被另一个线程B抢占,线程B对共享变量完成一次自增,等到线程A继续完成步骤3时,就丢失了线程B的这次操作。

要解决上面的多线程如何正确使用共享变量的问题,通常需要加锁,或者使用原子变量。

先来看X86架构下面原子变量的定义:

typedef struct {
    volatile int counter;
} atomic_t;  

原子类型其实是 int 类型,只是使用volatile禁止寄存器对其暂存。

原子操作的API:

atomic_read(atomic_t * v);                   // v
atomic_set(atomic_t * v, int i);             // v = i
void atomic_add(int i, atomic_t *v);         // v += i
void atomic_sub(int i, atomic_t *v);         // v -= i
void atomic_inc(atomic_t *v);                // v++
void atomic_dec(atomic_t *v);                // v--
int atomic_dec_and_test(atomic_t *v);        // (--v)==0?true:false
int atomic_inc_and_test(atomic_t *v);         // (++v)==0?true:false
int atomic_sub_and_test(int i, atomic_t *v);  // (v-i)==0?true:false
int atomic_add_negative(int i, atomic_t *v);  // (v+i)<0?true:false
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t * v);
int atomic_dec_return(atomic_t * v);
时间: 2024-10-09 20:30:56

extern、static、volatile 关键字的相关文章

java中 static,final,transient,volatile关键字的作用

static 和final static  静态修饰关键字,可以修饰 变量,程序块,类的方法: 当你定义一个static的变量的时候jvm会将将其分配在内存堆上,所有程序对它的引用都会指向这一个地址而不会重新分配内存: 修饰一个程序块的时候(也就是直接将代码写在static{...}中)时候,虚拟机就会优先加载静态块中代码,这主要用于系统初始化:当修饰一个类方法时候你就可以直接通过类来调用而不需要新建对象. final 只能赋值一次:修饰变量.方法及类,当你定义一个final变量时,jvm会将其

就是要你懂Java中volatile关键字实现原理

原文地址http://www.cnblogs.com/xrq730/p/7048693.html,转载请注明出处,谢谢 前言 我们知道volatile关键字的作用是保证变量在多线程之间的可见性,它是java.util.concurrent包的核心,没有volatile就没有这么多的并发类给我们使用. 本文详细解读一下volatile关键字如何保证变量在多线程之间的可见性,在此之前,有必要讲解一下CPU缓存的相关知识,掌握这部分知识一定会让我们更好地理解volatile的原理,从而更好.更正确地地

Java并发编程:volatile关键字解析(转)

volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用vola

Java并发编程:volatile关键字解析

volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用vola

volatile关键字及内存可见性

先看一段代码: package com.java.juc; public class TestVolatile { public static void main(String[] args) { ThreadDemo td = new ThreadDemo(); new Thread(td).start(); while(true){ if(td.isFlag()){ System.out.println("----------------"); break; } } } } cla

volatile关键字解析

转载:http://www.cnblogs.com/dolphin0520/p/3920373.html volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来

6、Java并发编程:volatile关键字解析

Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatil

内存管理_深入剖析volatile关键字

四.深入剖析volatile关键字 在前面讲述了很多东西,其实都是为讲述volatile关键字作铺垫,那么接下来我们就进入主题. 1.volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 2)禁止进行指令重排序. 先看一段代码,假如线程1先执行,线程2后执行: //线程1 boolean stop =

java并发之volatile关键字(TIJ21-21.3.3 21.3.4)

** 1 简介    volatile是java中的一个保留关键字,它在英语中的含义是易变的,不稳定的.volatile像final.static等其他修饰符 一样,可以修饰class中的域,而不能修饰方法中的局部变量.当修饰class中的域时,volatile可以修饰primative类型或者任意对 象.下面这个例子展示了这一点: public class TIJ_volatile {    private volatile int i;    private volatile String