可见性

一个线程对主内存的批改能够及时被其它线程察看到

导致共享变量在线程间不可见的起因

  • 线程穿插执行
  • 指令重排序加上线程穿插执行
  • 共享变量更新后的值没有在工作内存与主存间及时更新
  • 保障可见性和原子性
对于可见性Java提供了synchonizedvolatile

volatile

通过退出内存屏障和禁止重排序优化来实现,保障可见性不保障原子性
volatile变量进行写操作时,会在写操作后退出一条store屏障指令,将工作内存变量值刷新到主内存。

volatile变量进行读操作时,会在读操作前退出一条load屏障指令,从主内存读取共享变量。

通过下面两点,任何时候,不同线程总能看到该变量的最新值.所有的操作都是CPU级别的。

并不是说应用了volatile就线程平安了

package com.keytech.task;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class VolatileTest {    private static Integer clientTotal=5000;    private static Integer threadTotal=200;    private static volatile Integer count=0;    public static void main(String[] args) {        ExecutorService executorService = Executors.newCachedThreadPool();        Semaphore semaphore=new Semaphore(threadTotal);        for (int i = 0; i < clientTotal; i++) {            executorService.execute(()->{                try{                    semaphore.acquire();                    update();                    semaphore.release();                }catch (Exception e){                    e.printStackTrace();                }            });        }        executorService.shutdown();        System.out.println("count:"+count);    }    private static void update(){        count++;    }}//count:4988
尽管应用了volatile,然而线程不平安。起因:update是非原子性的。
 private static void update() {     count++; //分3步     //1.取出以后count值     //2.count + 1     //3.count 从新写回主存 }
假如同时有两个线程进行操作,两个线程同时执行到第一步(从内存中读取最新值)失去一样的最新的后果,而后进入第二步(+1操作)并进行第三步(从新写回主存)。只管第一步获取的值是一样的,然而同时将+1后的操作写回主存,这样就会丢掉某个+1的操作,这样就会呈现线程不平安问题

总结

  • volatile进行多线程加是线程不平安的,不适宜计数
  • volatile不具备原子性

volatile的应用场景

  • 对变量的写操作不依赖以后值
  • 该变量没有蕴含在其它变量的不变式子中
  • volatile适宜作为状态的标记量
volatile boolean flag = false;//线程1context = loadContext();flag = true;//线程2while(!flag){    sleep();}todo(context);