volatile遇到的问题

 

 1 public class VolatileThread implements Runnable {
 2 
 3     private volatile int a = 0;
 4 
 5     @Override
 6     public void run() {
 7 //        synchronized (this){//
 8         a = a + 1;
 9 //            System.out.println(Thread.currentThread().getName()+"----"+a);
10         try {
11             Thread.sleep(100);
12         } catch (InterruptedException e) {
13             e.printStackTrace();
14         }
15 //            System.out.println(Thread.currentThread().getName() + "####"+a);//
16         a = a + 1;
17         System.out.println(Thread.currentThread().getName() + "****" + a);
18 
19 //        }
20     }
21 }
 1 public class VolatileTest {
 2     public static void main(String[] args) {
 3         VolatileThread volatileThread = new VolatileThread();
 4 
 5         for(int i=0;i<4;i++){
 6             Thread t = new Thread(volatileThread);
 7             t.setName("Thread-t"+i);
 8             t.start();
 9         }
10     }
11 }
以上代码,代码1是一个线程类,其中成员变量a被volatile修饰;代码2是main函数类,创建了4个线程。执行main函数,偶然发生了诡异结果现象:

Thread-t3****6
Thread-t0****7
Thread-t1****6
Thread-t2****6

疑问1:为什么会出现重复数字?疑问2:为什么7会现在比6先输出的现象?


 先简单了解下volatile(下一篇将重点介绍volatile和Synchronized,本篇只是简单介绍下):

什么是volatile?volatitle起什么作用?

volatile是与java内存模型(JMM)有关的一个关键字,用来修饰成员变量,起作用:1、保证共享变量(成员变量)的可见性;2、指令有序性(禁止指令重排)。同Synchronized相比,它是轻量级,但不具有原子性,因此对于多线程来说,不是线程安全的。

如何理解可见性?

我们知道,每个线程都有自己的私有工作内存,在执行线程时,会从主内存拷贝一份成员变量到自己的工作内存,也就是变量副本,然后执行程序指令,最后把副本变量刷回主内存。对于A和B两个线程,同时要针对成员变量进行修改,比如a++,如果A线程执行完后还来及刷回主内存,B线程就开始拷贝原主内存的变量到自己的工作内存执行a++,此时,A线程所修改的变量对B线程而言是不可见的。而volatile的功能之一就是保证共享变量的可见性。


疑问1解析: 

看看上面的代码,首先四个线程分别执行了第8行 a=a+1 后,分别进入sleep休眠,此时主内存的变量是4,线程分别休眠过期后,开始争夺cpu时间片,执行第16行的 a=a+1,理论上来说,上面的输出结果不会出现重复的变量数值,这又是为什么呢?原因要从a=a+1这个操作讲起,a=a+1,类似a++,这个操作并不是原子性操作,实际上是由从主内存中读取值、工作内存中+1操作、操作结果刷回主内存三步操作组成,在一条线程中,他们任意一步被打断,因此保证不了主内存中a一定+1,出现上面重复变量数值的结果。也就是我们常常说的volatile并不能保证线程安全。

疑问2解析:

有人会说,多线程在不加锁情况下,打印是随机的,原因是os处理器调度随机的。从这层面上理解是可以的,但作为程序员,为什么他是随机的,这期中有没有什么规则?我觉得务必要一探究竟。首先看下面println()的底层源码

public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
看到Synchronized,不得不解释下,sync他本质上也是个非公平锁,也就是说多线程下,谁先拿到锁并不确定,当然谁拿到锁,谁就先打印。sync在Java1.6被称为一种重量级的锁,事实上,它底层的确涉及到的知识很多。还是耐心练功夫吧。

 

原文地址:https://www.cnblogs.com/light-sunset/p/12865236.html