• 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被称为一种重量级的锁,事实上,它底层的确涉及到的知识很多。还是耐心练功夫吧。

     

  • 相关阅读:
    mysql数据库查询库中所有表所占空间大小
    mysql行转列
    mysql重置密码
    POJ1426 Find The Multiple —— BFS
    POJ3279 Fliptile —— 状态压缩 + 模拟
    POJ1077 Eight —— IDA*算法
    POJ1077 Eight —— A*算法
    POJ1077 Eight —— 双向BFS
    POJ1077 Eight —— 反向BFS
    POJ1077 Eight —— 正向BFS
  • 原文地址:https://www.cnblogs.com/light-sunset/p/12865236.html
Copyright © 2020-2023  润新知