内存可见性问题volatile|原子性apI|JUC Api

   1. 内存可见性问题: ??

 现象:主存中数据改了,但是Main线程中获取的数据没有跟新?

package com.denganzhi.pp;

public class Main {
	
	public static void main(String[] args) {
		ThreadDemo threadDemo=new ThreadDemo();
		new Thread(threadDemo).start();
		while(true){
		//	synchronized(threadDemo){
				if(threadDemo.isFlag()){
					System.out.println("--main---");
					break;
				}
		//	}
		}
	}
}

class ThreadDemo implements Runnable{
	//private volatile boolean flag=false;
	private boolean flag=false;
	@Override
	public void run() {
	  try {
		Thread.sleep(200);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	  flag=true;
	  System.out.println("ThreadDemo->flag:"+flag);
	}
	public boolean isFlag() {
		return flag;
	}
}

 执行结果: Main 线程 while 循环中获取的 是 false


问题原因:

   private boolean flag=false; 
    是多个线程共享数据,存在主存中,堆内存中
    Java 每一个线程运行在栈中, 
    线程1 获取flag 读到自己的栈中,改掉,flag=true,然后在写到主存中去
    Main线程 上来读取 主存数据 flag=false, 
    Main线程 后面 while(true) 该代码系统底层执行效率非常高,
    不会进行系统调用,永远取的是 Main线程中数据, 返回的是false   

 解决方法1:
    synchronized(threadDemo)  
    保证Main线程每次执行的时候,都到主存中更新一下
    但是synchronized效率低,如果一个线程持有锁,另外一个线程阻塞,等待cpu调度
    

package com.denganzhi.pp;

public class Main {
	
	public static void main(String[] args) {
		ThreadDemo threadDemo=new ThreadDemo();
		new Thread(threadDemo).start();
		while(true){
			synchronized(threadDemo){
				if(threadDemo.isFlag()){
					System.out.println("--main---");
					break;
				}
			}
		}
	}
}

class ThreadDemo implements Runnable{
	//private volatile boolean flag=false;
	private boolean flag=false;
	@Override
	public void run() {
	  try {
		Thread.sleep(200);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	  flag=true;
	  System.out.println("ThreadDemo->flag:"+flag);
	}
	public boolean isFlag() {
		return flag;
	}
}

运行结果:  Main线程获取true


    解决效率低问题:
    直接操作主存中数据,使用volatile 关键字解决问题

package com.denganzhi.pp;

public class Main {
	
	public static void main(String[] args) {
		ThreadDemo threadDemo=new ThreadDemo();
		new Thread(threadDemo).start();
		while(true){
		//	synchronized(threadDemo){
				if(threadDemo.isFlag()){
					System.out.println("--main---");
					break;
				}
		//	}
		}
	}
}

class ThreadDemo implements Runnable{
	private volatile boolean flag=false;
	@Override
	public void run() {
	  try {
		Thread.sleep(200);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	  flag=true;
	  System.out.println("ThreadDemo->flag:"+flag);
	}
	public boolean isFlag() {
		return flag;
	}
}

输出结果:Main线程获取true

volatile问题: 
  1. volatile 不具备 "互斥性"
  2. volatile 不能保证 变量的 "原子性"

    2. 什么是原子性:

   int i=0;
   i++  
    int temp =i ;   // i存储主存中, 线程A读存入temp
   i=i+1;         // 线程A执行
   i= temp;       // 线程A把 值 写入主存中
   上面是3个操作不可以分隔, 叫做原子性
  
   但是 在线程A 读取的时候线程 B也读取了,
   A 写入数据的时候, 写入以后主存数据变1
   B 也写入, 写入以后主存储数据变1 
   破坏原子性  
   这个时候会有2个重复数据 ,比如下列代码:

package com.denganzhi.pp;

public class Main2 {

	 public static void main(String[] args) {
		 AtomicDemo auAtomicDemo=new AtomicDemo();
		 for (int i = 0; i < 10; i++) {
			new Thread(auAtomicDemo).start();
		}
		
	}
}
class AtomicDemo implements Runnable{
	private int serialNumber=0;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getId()+ " name:" + getSerialNumber());
	}
	public int getSerialNumber() {
		return serialNumber++;
	}
	
}

 执行 结果:

出现重复数据,原子性被破坏        
CAS算法: JVM 底层 对CAS算法 进行了支持,解决上面问题,但是没有解决原子性问题
 多次线程可以同时执行,只有其中一个 线程 可以执行成功,但是不会阻塞线程 
 原理:
   内存值  V
   预估值  A
   更新值  B
   当且 当 V==A 时,  V=B ,否则不做任何操作
 例子:

   线程A:
读1   V=0 
sn=sn+1  
读2   A=0,在写入之前读取,V==A 
B=1  写入 主存中
    线程B:
读1  V=0
sn=sn+1
读2  A=1    V != A
写入失败
线程C....失败
线程D... 失败,失败了,但是没有阻塞,CPU还可以继续调度,就是由于这个原因,优于 synchronized

使用 CAS 算法解决上面问题:

 * jdk1.5 原子性提供api接口:
  *java.util.concurrent.atomic  下提供 类  实现了CAS 算法,来解决原子性,并没有解决原子性(要解决只能使用sychronized)
AtomicBoolean 
AtomicInteger:        Int类型
AtomicIntegerArray     数组
AtomicIntegerFieldUpdater    
AtomicLong 
AtomicLongArray 
AtomicLongFieldUpdater 
AtomicMarkableReference 
AtomicReference 
AtomicReferenceArray 
AtomicReferenceFieldUpdater 
AtomicStampedReference

  代码实现:

 public class Main2 {
  // 实现抢票 用例
  public static void main(String[] args) {
		 AtomicDemo auAtomicDemo=new AtomicDemo();
		 Thread thread=null;
		 for (int i = 0; i < 7; i++) {
			 thread=new Thread(auAtomicDemo);
			 thread.setName("线程:"+i );
			 thread.start();
		}
		
	}
 }
 class AtomicDemo implements Runnable{
	// 还 剩下 5 张票 ,7个人来 抢票
	AtomicInteger automicInteger=new AtomicInteger(5);
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		int num=getSerialNumber();
		if(num <= 0){
			System.err.println(Thread.currentThread().getName() +" 没有抢到火车票");
		}else{
			System.out.println(Thread.currentThread().getName() +" 抢到第" + num + "张火车票");
		}
	
	}
	public int getSerialNumber() {
		return automicInteger.getAndDecrement(); //--
	}
} 

线程:4 抢到第2张火车票
线程:3 抢到第1张火车票
线程:0 没有抢到火车票
线程:1 抢到第5张火车票
线程:6 抢到第3张火车票
线程:2 抢到第4张火车票
线程:5 没有抢到火车票

3. JUC 问题:

   jdk1.5 以后 Java 提供了JUC 处理多线程 
java.util.concurrent.* 下 ConcurrentHashMap 也实现线程安全的,使用Java8 CAS实现【底层C】
 都是 哈希表 :功能对比 
 java.util.concurrent.*  包下类 在高并发 效率高比 java.utils.hashmap 【简称 juc】
   LIst、hashmap:  线程不安全 
   Vector、hashtable:线程安全的,底层使用锁实现,只有一个线程可以访问hashtable 
   Jdk1.5以后,解决效率低问题:
   CopyOnWriteArraySet/CopyOnWriteArrayList/ConcurrentHashMap 使用 CAS算法实现底层

 public class Main {
	/**
	 *  每一个线程有个 一个ThreadLocal
	 *  为单独线程 共享数据
	 */
	public static void main(String[] args) {
	/**
		 * jdk1.5 以后 Java 提供了JUC 处理多线程 
		 *  线程安全容器
		 *  Vector,Hashtable 线程安全容器,使用syschronized实现
		 * java.util.concurrent.* 下 ConcurrentHashMap 也实现线程安全的,使用Java8 CAS实现【底层C】
		 *   都是 哈希表 :功能对比 
		 *    java.util.concurrent.*  包下类 在高并发 效率高比 java.utils.hashmap 【简称 juc】
		 */
		ConcurrentHashMap<String, String> con=new ConcurrentHashMap<>();
		con.put("name", "小明");
		System.out.println(con.get("name"));
	}
}
发布了111 篇原创文章 · 获赞 123 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/dreams_deng/article/details/105105034
API