在多线程使用volatile关键字

构建了100个线程, Volatile变量具有 synchronized 的可见性特性,但是不具备原子特性。
同时访问操作i,如果i在使用i++时候是对自身变量的操作,实际它这样的操作不是原子操作。下边我们用一段代码试试。
/**
 * Java语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。 其中 Volatile
 * 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。
 * 
 * @author Janle
 *
 */
public class volatile关键字 {

	public static void main(String[] args) {
		Thread threads[] = new Thread[100];
		for (int i = 0; i < threads.length; i++)
			// 建立100个线程
			threads[i] = new ThreadVolatile();

		for (int i = 0; i < threads.length; i++) 
			// 运行刚才建立的100个线程
            threads[i].start();
		for (int i = 0; i < threads.length; i++) {
			try {
				threads[i].join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		/**
		 * 如果对n的操作是原子级别的,最后输出的结果应该为n=1000,很多时侯输出的n都小于1000.这说明n=n+1不是原子级别的操作。
		 * 原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,
		 * 也就是说如下的表达式都不是原子操作:
		 * n = n + 1;
		 * n++;
		 */
		System.out.println("n=" + ThreadVolatile.n);
	}
}

class ThreadVolatile extends Thread {
	public static volatile int n = 0;
	public static synchronized void inc() {
        n++;
    }
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				/**
				 * 这里注意:
				 * 当变量的值由自身的上一个决定时,如n=n+1、n++等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,
				 * 如n = m + 1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。
				 */
				//n = n + 1;
				inc();//// n = n + 1 改成了 inc();使用这个才能保证是原来的操作,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作
				sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

总结:
用写操作时候不能依赖于当前值【当前获得的值不能被修改】。
该变量没有包含在具有其他变量的计算公式中。

volatile修饰设计用来修饰被不同线程访问和修改的变量。如果不加入volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。

猜你喜欢

转载自janle.iteye.com/blog/2289556