并发编程三大特性,针对Volatile关键字的分析及Java实例

我是一个从汽车行业转行IT的项目经理,我是Edward,如想了解更多,请关注我的公众号【转行项目经理的逆袭之路】。今天跟大家聊聊Volatile关键字。

并发编程三大特性:

在这里插入图片描述
概念听上去很虚对不对,我们用三个实例来理解一下:

1.可见性:

package com.mashibing.juc.c_012_volatile_visible;

import java.util.concurrent.TimeUnit;

/**
 * volatile关键字,使一个变量在多个线程间可见
* @author EP
* @date 2020年4月10日  
* @version 1.0
 */

public class T01_HelloVolatile {
	/*volatile*/ boolean running = true;
	void m() {
		System.out.println("m start");
		while (running) {
			
		}
		System.out.println("m end!");
	}
	
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		T01_HelloVolatile t = new T01_HelloVolatile();
		new Thread(t::m, "t1").start();    //lambda
		
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t.running = false;    
		/*
		 * 不加volatile时,main线程中running属性的变化,
		 * t1线程中执行m方法中是看不见的,while会无限循环
		 */

	}

}
  • 不加volatile时,main线程中running属性的变化,
  • t1线程中执行m方法中是看不见的,while会无限循环

2.有序性:

package com.turning.juc.c_012_volatile_serial;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
 * 验证volatile保证指令有序性
* @author EP
* @date 2020年4月12日  
* @version 1.0
 */

public class VolatileSerialTest {
	
	static /*volatile*/ int x=0,y=0;     //加了volatile修饰后,不会再出现a:1,b:1的情况,保证了有序性
	
	public static void main(String[] args) {
		Set<String>resultSet = new HashSet<String>();
		Map<String, Integer>resultMap = new HashMap<String, Integer>();
		
		for (int i = 0; i < 1000000; i++) {
			x=0; y=0;
			resultMap.clear();
			Thread one = new Thread(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					int a = y;
					x = 1;
					resultMap.put("a", a);
					
				}
			});
			
			Thread other = new Thread(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					int b = x;
					y=1;
					resultMap.put("b", b);
					
				}
			});
			
			one.start();
			other.start();
			try {
				one.join();
				other.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			resultSet.add("a:"+resultMap.get("a")+","+"b:"+resultMap.get("b"));
			System.out.println(resultSet);  //4种结果都出现了,尤其是a:1,b:1这个结果,说明指令发生了重排序
		}
	}
}

加了volatile修饰后,不会再出现a:1,b:1的情况,保证了有序性

3.不具备的原子性:

package com.mashibing.juc.c_013_synchronized_atomic;

//import java.util.ArrayList;
//import java.util.List;

/**
 * volatile并不能保证多个线程共同修改running变量时所带来的的不一致问题,也就是说volatile不能
 * 替代synchronized
 * 运行下面程序,并分析结果
* @author EP
* @date 2020年4月12日  
* @version 1.0
 */

public class T {
	volatile int count = 0;    //只能保证可见性,无法保证原子性
	/*synchronized*/ void m() {       //不加synchronized无法保证原子性
		for (int i = 0; i < 10000; i++) {
			count++;
		}
	}

	public static void main(String[] args) {
		T t = new T();
		
		Thread[]threads = new Thread[10];
		for (int i = 0; i < threads.length; i++) {
			threads[i] = new Thread(t::m);
			threads[i].start();
		}
		
		for (Thread thread : threads) {
			try {
				thread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(t.count);   //到不了100000
		
//		List<Thread>threads = new ArrayList<Thread>();
//		
//		for (int i = 0; i < 10; i++) {
//			threads.forEach((o)->{
//				try {
//					o.join();
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//			});
//			System.out.println(t.count);
//		}

	}

}

不加synchronized结果永远到不了100000。

画图理解:
在这里插入图片描述
由于该m方法没有被保证原子性,线程2在线程1尚未完成之前就已经开始了部分累加动作,而线程2部分累加动作在与主内存同步的过程中被失效作废了。

原创文章 46 获赞 7 访问量 2074

猜你喜欢

转载自blog.csdn.net/EdwardWH/article/details/105473236
今日推荐