zoukankan      html  css  js  c++  java
  • volatile和synchronized关键字

    synchronized

    java课上讲到过synchronized

    首先看看用synchronized和没用synchronized的区别

    import lombok.Getter;
    /**
     * @author yintianhao
     * @createTime 20190123 16:28
     * @description synchronized
     */
    public class SYN {
        @Getter
        private int count;
        private Object lock;
        public SYN(){
            count = 0;
            lock = new Object();
        }
        public void noSyn(){
            for (int i = 0;i < 100;i++){
                count++;
            }
        }
        public void Syn(){
            synchronized (lock){
                for (int i = 0;i < 100;i++){
                    count++;
                }
            }
        }
    }
    View Code

    要进行的操作是对count进行自增操作,执行一次里面的Syn和noSyn函数都是对count这个变量进行加一百次的操作

    不同的就是,Syn函数用了synchronized

    然后开启十个线程来执行这两个函数,之后打印出count的值,我们期望的是count等于1000

    List<Thread> threads = new ArrayList<>();
            SYN syn = new SYN();
            for (int i = 0;i < 10;i++){
                threads.add(new Thread(()->syn.noSyn(),"thread-"+i));
            }
            for (Thread o:threads)
                o.start();
            for (Thread o:threads)
                o.join();
            System.out.println(syn.getCount());
    View Code

    再看运行结果

    可是结果却不是1000

    然后再把运行的函数改成Syn(),再次看结果

    1000符合预期,为了避免偶然,重复运行几次结果仍是1000

    那么为什么这里结果不同的呢,原因就是牵涉到另外一个概念,锁,在这个例子中,SYN类中的lock对象就可以看做一把锁

    只有拿到锁,才能执行synchronized代码块中的代码块,所以当一个线程在执行synchronized里面的代码并且还没把锁交出来(释放)时,其他线程无法获取

    到锁,故无法执行其中的代码,在没有使用synchronized的函数中,因为没有锁机制,有可能for循环还没结束的时候就有新的进程进来,即没有加到100就进来

    故count最后不会到1000,而且也不是一个固定的数字

    synchronized的实现原理

    synchronized有三种用法:

    第一种是对于同步方法,即修饰某个函数,这个时候锁对象是当前类的实例化对象

    第二种是对于同步静态方法,这个时候锁是当前类的Class对象

    第三种就是我上面的用法,对于同步代码块,锁是括号里的对象

    前面说一个进程会获得锁,那么这个锁是存在在什么地方呢

    首先需要了解java对象头,对象头分为两种

    普通对象的对象头占两个字宽

    数组对象的对象头占三个字宽,相比于普通对象的对象头多了一个字宽来存储数组长度

    java对象头中的Markword字段中存储对象的HashCode,分代年龄和锁标记位,在运行期间,MarkWord存储的数据会随着锁标志位

    的变化为变化,MarkWord的结构是这个样子的

    这个锁的标志位就是锁的类型了,也就是之前所说的"锁" 

    Volatile

    java对volatile的定义是:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量,

    如果一个字段被申明为volatile,线程内存模型确保所有线程能够看到的这个变量的值是一致的

    那么volatile是怎么保证可见性的呢

    我们知道处理器为了保证处理的速度,是不和系统内存直接通信的,因为内存的速度远远慢yu每个处理器先是将系统内存中的数据读入到CPU内部缓存中

    再进行操作,但是操作完成之后是不知道什么时候缓存中的数据会写到内存中的,那么其他处理器读取这个变量的时候就有可能读取到旧的数据,volatile是

    这样子做的,一旦被volatile修饰的变量发生读写操作,就会发生两个动作:

    1,将当前处理器缓存行的数据写到系统内存中(这是Lock前缀指令会引起的)

    2,然后这个操作使得其他CPU缓存了该内存地址的数据无效

    然后我们知道的是,每个处理器中的缓存是一致的,那么这个时候就需要缓存一致性协议,每个处理器嗅探总线上传播的数据来检测自己缓存中的值是否过期,

    当发现自己的缓存行对应的内存地址被修改,就会将自己缓存行设置为无效,当需要修改的时候,重新从系统内存读取到缓存行,这样就实现了可见性

    关于volatile变量的原子性,单个volatile变量的读写是具有原子性的,但是如果是类似于上面代码中的++这种复合操作,则不具有原子性

  • 相关阅读:
    如何高效的学习技术
    面试连环炮系列(二十三): StringBuffer与StringBuild的区别
    面试连环炮系列(二十二):常用的设计模式有哪些
    算法天天练709:字符串转小写
    面试连环炮系列(二十一):你们的项目怎么使用kafka
    算法天天练771:查找字符串出现的次数
    初次进入职场如何工作与学习
    面试连环炮系列(二十):TCP的滑动窗口协议是什么
    算法天天练334:字符串翻转
    面试连环炮系列(十九):分布式锁的实现方案
  • 原文地址:https://www.cnblogs.com/Yintianhao/p/10311765.html
Copyright © 2011-2022 走看看