java 语言多线程可见性(synchronized 和 volatile 学习)

共享变量可见性实现的原理
java 语言层面支持的可见性实现方式: synchronized volatile


1、 synchronized 的两条规定:
1 线程解锁前,必须把共享变量的最新值刷新到主内存中。
2 线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁需要是同一锁)
线程解锁前对共享变量的修改在下次加锁时对其他线程可见。

2、 volatile 实现可见性
深入来说,通过加入内存屏障禁止重排序优化来实现 的。
对volatile变量执行写操作时,会在写操作后加入一条store屏障指令。
对volatile变量执行读操作时,会在读操作前加入一条load 屏障指令。
通俗地讲,volatile 变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会
强迫线程将最新的值刷新到主内存中,这样任何时刻,不同的线程总能看到该变量的最新值 。
但volatile 不能保证变量的原子性。

典型的num++ 操作,使用volatile修饰变量时,无法保存操作的原子性,所以会出现很多值的情况。
如果出现这种情况,还需要使用volatile时解决办法就是:

使用synchronized 关键字 进行 包装。ReentrantLock 进行代码前的加锁,然后使用unlock 释放锁。

在使用volatile 的时候注意专场合:

  • 1、对变量的写入操作不依赖其当前的值。典型的就是numb++ \ num=num*3 等 。
  • 2、该变量没有包含在具有其他变量的不变式中。(例如两个volatile 变量 存在大小关系中,或其他关系中等)

3、 synchronized 和 volatile 比较

  • 1、volatile 不需要加锁,比synchronized 更经量级,不会阻塞线程。
  • 2、从内存可见性角度讲,volatile 读相当于加锁,volatile 写相当于解锁。
  • 3、synchronized 即能保证可见性,又能保证原子性,而volatile 只能保证可见性,无法保证原子性。

代码示例:

package org.apple.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class VolatileTest {
    
    private Lock lock = new ReentrantLock();
    
    private volatile int number = 0;
    
    public void increase(){
          this.number++;
        // 第二种解决办法 
//        lock.lock();
//        this.number++;
//        lock.unlock();
        // 第一种解决办法
        //synchronized(this){
        //    this.number++;
        //}
    }
    
    public int getNumber(){
        
        return this.number;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        final VolatileTest volatileTest = new VolatileTest();
        for (int i = 0; i < 500; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    volatileTest.increase();
                }
            }).start();
        }
        // 如果当前线程还有子线程在运行,主线程则让出cpu 资源,直到所有的子线程运行完成后,则往下继续执行。
        if(Thread.activeCount()>1){
            Thread.yield();
        }
        System.out.println(Thread.activeCount());
        System.out.println(volatileTest.getNumber());
    }

}

 

posted @ 2015-04-08 23:17  慎初  阅读(259)  评论(0编辑  收藏  举报