现在的位置: 首页 > 综合 > 正文

Java中的原子操作和volatile关键字

2013年06月10日 ⁄ 综合 ⁄ 共 2277字 ⁄ 字号 评论关闭

 http://hi.baidu.com/yicun05/blog/item/ce8cf118cec87172dbb4bdd3.html

1:什么是原子操作?
线程执行的最小单位,不能中断。

2:volatile原理是什么?
一般变量,在主存和当线程的临时内存中都一份缓存,为保证数据
 统一,需要同步,当同步没有完成时,如果其他线程读取主存上的值就会导致不同步异常。

Volatile就是告诉处理器这个变量是不稳定的,不要在线程的临时内存缓存该数据,直接使用JVM内存的数据,就避免了同步的步骤。

3:对volatile变量数据进行修改(不是重赋值),是否能保证同步?

不能。即使是++,--也不是原子操作,会因为CPU的线程的切换,导致数据不可控。 

4:那为什么还要用volatile这种修饰符?
volatile变量读取,和赋值是可以保证原子性


5:那到底如何解决这样的问题?
        第一种:采用同步synchronized解决,这样虽然解决了问题,但是也降低了系统的性能。
        第二种:采用原子性数据Atomic变量,这是从JDK1.5开始才存在的针对原子性的解决方案,这种方案也是目前比较好的解决方案了。

6:Atomic的实现基本原理? 
 * 1)Atomic中的变量是申明为了volatile变量的,这样就保证的变量的存储和读取
 * 是一致的,都是来自同一个内存块,
 * 2)Atomic对变量的++操作进行了封装,提供了getAndIncrement方法,
 * ,并提供了compareAndSet方法,来完成对单个变量的加锁和解锁操作,
 * Atomic虽然解决了同步的问题,但是性能上面还是会有所损失,不过影响不大。

 

7:volatile 是否可以修饰对象实例?为什么对象变量重新赋值也是原子操作?

可以。JVM保证volatile的读操作一定发生在写操作之后,即使没有使用sycnrhonized语块。

因为在栈中,对象变量存储的是一个地址,该地址是指向堆中对象的,所以操作并不负责,

可以认为对一个对象重新赋值是原子操作。

8:为什么在Java中变量赋值中,除了long和double类型的变量外都是原子操作?

由于long和double类型是大于32BIT的,JVM把他们作为2个原子性的32位值来对待,需要进行2次赋值。所以,不是原子操作。
如果,硬件和软件都支持64BIT,则long和double可能就是原子操作。

 

以下代码起100个线程对AtomicInteger 和volatile 等类型数据,进行1000次的递增,如果是线程安全的最终的值应该是100×1000即100000。否则即表明不是线程安全的。

 

public class TestAtomic {    
        
    
    public static AtomicInteger astom_i = new AtomicInteger();    
        
    public static volatile Integer v_integer_i = 0;    
        
    public static volatile int v_i = 0;    
        
    public static Integer integer_i = 0;    
        
    public static int i = 0;    
        
    public static volatile int endThread = 0;    
        
    public static void main(String[] args) {    
        new TestAtomic().testAtomic();    
     }    
        
    public void testAtomic() {    
            
        for(int i=0; i<100; i++) {    
            new Thread(new IntegerTestThread()).start();    
         }    
            
        try {    
            for(;;) {    
                 Thread.sleep(500);    
                if(TestAtomic.endThread == 100) {    
                     System.out.println(">>Execute End:");    
                     System.out.println(">>Atomic: \t"+TestAtomic.astom_i);    
                     System.out.println(">>VInteger: \t"+TestAtomic.v_integer_i);    
                     System.out.println(">>Integer: \t"+TestAtomic.integer_i);    
                     System.out.println(">>Source i: \t"+TestAtomic.i);    
                     System.out.println(">>Source Vi: \t"+TestAtomic.v_i);    
                    break;    
                 }    
             }    
                
         } catch (Exception e) {    
             e.printStackTrace();    
         }    
     }    
        
}    
class IntegerTestThread implements Runnable {    
    final public static Lock lock=new ReentrantLock();
    public void run() {    
        int x = 0;    
        while(x<1000) {    
             TestAtomic.astom_i.incrementAndGet();    
             TestAtomic.v_integer_i++;    
             TestAtomic.integer_i++;    
             TestAtomic.i++;    
             TestAtomic.v_i++;    
             x++;    
         }
        //以下其实使用,AtomicInteger就能解决
        lock.lock();
         ++TestAtomic.endThread;    
         System.out.println("Thread end: "+TestAtomic.endThread);
         lock.unlock();
     }    
} 

 

抱歉!评论已关闭.