精华内容
下载资源
问答
  • 看到书上对于原子性的定义是这样:不会有中间状态存在,要么什么都没改变,要么全都改变。那既然synchronized保证原子性,(方法执行完在刷回主存)那为什么我其他线程能读取到同步代码块的中间状态?
  • 对于Java多线程,接触最多的莫过于使用synchronized,这个简单易懂,可谓一招吃遍天下,可能在一些特殊场景...作为Java中的轻量级锁,当多线程中一个线程操作后可以保证其他线程可见,也就是书上所说的“可见”...

     

    对于Java多线程,接触最多的莫过于使用synchronized,这个简单易懂,可谓一招吃遍天下,可能在一些特殊场景下确需要用一些其他的同步进行。今天我就简单介绍一下几种锁,其实像类似的博客和文章和很多,所以也不会多赘述,我更想抛出在实际应用中所碰到的问题。好,废话少说。

    volatile

    作为Java中的轻量级锁,当多线程中一个线程操作后可以保证其他线程可见,也就是书上所说的“可见性”,另外一个就是“重排序”。所谓重排序指的是JVM对指令的优化。很多人可能在实际实验中发现好像不是如此,最后的例子我也会说明这一点。

     

    synchronized

    这个作为Java中“重量级”的线程安全机制被大家所熟知。有些Java开发可能不知道volatile,也不太会去使用。但是如果不知道synchronized,估计这种情况就比较少了。

     

    java.util.concurrent.locks.ReentrantLock

    java.util.concurrent.中是JDK1.5中出的对于一些并发操作的类库,其中包括很多同学很喜欢的原子类,比如说AtomicInteger,我在这里也不会对其原理做过多的分析,因为我也还没有去看其源代码,所以也不好讲,也不是今天我想要讲的重点。

    好,说一下ReentrantLock,这个锁主要是能显示的添加锁和释放锁,好处是更加灵活,能够更加准确的控制锁,也能确保系统的稳定,比如说“重连”。后面代码会有使用到。

     

    项目中如果没有特殊需求除了synchronized其他也比较少用,各位编程爱好者可以在自己的项目中多使用其他锁机制以了解其工作原理和优劣。技术一定会精进不少。

    实际场景

     

    package com.yuzhipeng.m07.thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 
     * @author Yu zhipeng | 2012-07-26
     *
     */
    public class TestMultiThread  implements Runnable{
    
    	private static int i;
    	
    	private static volatile Integer vi = 0;
    	
    	private static AtomicInteger ai = new AtomicInteger();
    	
    	private static Integer si = 0;
    	
    	private static int ri;
    	
    	private static AtomicInteger flag = new AtomicInteger();
    	
    	private Lock lock = new ReentrantLock();
    	
    	@Override
    	public void run() {
    		for(int k=0;k<200000;k++){
    			i++;
    			vi++;
    			ai.incrementAndGet();
    			synchronized(si){
    				si++;
    			}
    			lock.lock();
    			try{
    				ri++;
    			}finally{
    				lock.unlock();
    			}
    			
    		}
    		flag.incrementAndGet();
    	}
    	
    	public static void main(String[] args) throws InterruptedException{
    		TestMultiThread t1 = new TestMultiThread();
    		TestMultiThread t2 = new TestMultiThread();
    		ExecutorService exec1 = Executors.newCachedThreadPool();
    		ExecutorService exec2 = Executors.newCachedThreadPool();
    		exec1.execute(t1);
    		exec2.execute(t2);
    		while(true){
    			if(flag.intValue()==2){
    				System.out.println("i>>>>>"+i);
    				System.out.println("vi>>>>>"+vi);
    				System.out.println("ai>>>>>"+ai);
    				System.out.println("si>>>>>"+si);	
    				System.out.println("ri>>>>>"+ri);	
    				break;
    			}
    			Thread.sleep(50);
    		}
    
    		
    	}
    	
    }
    

    输出结果:

    i>>>>>381890
    vi>>>>>353610
    ai>>>>>400000
    si>>>>>392718
    ri>>>>>392658

    从上面的输出结果来看真是让人大感意外,只有原子操作AtomicInteger的结果保证了多线程的安全性,而其他不管是用轻量级的volatile还是重量级的synchronized都没有达到我们想要的效果。这也让我产生了大在的怀疑。难道问题真的这么蹊跷?

    从这里不难看出除了AtomicInteger用的是其自己的方法而其他都是用到了Java的语法糖++操作。而这让我想起了++操作并非原子操作,而可能在其中间操作导致了其他线程对其他进行了修改,虽然同样的问题我在《Think in Java》中也找到可以佐证的例子。可是我在这里始终没有办法想通,因为我对si已经加了synchronized操作。难道还会有问题?这让我想把这段代码编译成字节码的冲动。好吧,下面看字节码(这里我单独把synchronized这一段操作抽出来,作为分析,其他几个就算了,不然编译后的字节码有点多)

    为了方便看,先贴出源代码

    public class TestSynchronizedThread  implements Runnable{
    
    	private static Integer si = 0;
    	
    	@Override
    	public void run() {
    		for(int k=0;k<200000;k++){
    			synchronized(si){
    				si++;
    			}
    		}
    	}
    }


    下面是字节码,为了节省篇幅,一些不重要的部分我将不贴出

       0:   iconst_0
       1:   istore_1
       2:   iload_1
       3:   ldc     #2; //int 200000
       5:   if_icmpge       55
       8:   getstatic       #3; //Field si:Ljava/lang/Integer;
       11:  dup
       12:  astore_2
       13:  monitorenter
       14:  getstatic       #3; //Field si:Ljava/lang/Integer;
       17:  astore_3
       18:  getstatic       #3; //Field si:Ljava/lang/Integer;
       21:  invokevirtual   #4; //Method java/lang/Integer.intValue:()I
       24:  iconst_1
       25:  iadd
       26:  invokestatic    #5; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       29:  dup
       30:  putstatic       #3; //Field si:Ljava/lang/Integer;
       33:  astore  4
       35:  aload_3
       36:  pop
       37:  aload_2
       38:  monitorexit
       39:  goto    49
       42:  astore  5
       44:  aload_2
       45:  monitorexit
       46:  aload   5
       48:  athrow
       49:  iinc    1, 1
       52:  goto    2
       55:  return

    从这里一看从monitorenter进入安全区到monitorexit出安全区没有发现si是处于中间状态的,那又是在哪出的问题呢?我已经无法解释了!!还请各位大侠指点。

     

    PS:

    这个问题肯定出在++操作上,如果我把锁对象换成其他公用对象,那这个问题得到解决。

     

     

    展开全文
  • 提纲 认识锁 锁的概念 悲观锁 概念:假定会发生并发冲突,同步所有对数据的... 实例:AtomicInt 使用自旋锁,保证数据的原子性 其实也是一种乐观锁 可重入锁 概念:同一个线程,在拿到一次锁之后,可以继续调用

    提纲


    认识锁

    锁的概念

    悲观锁

    • 概念:假定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁。
    • 实例:Synchronized 就是悲观锁

    乐观锁

    • 概念:假定没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,修改后重试修改。
    • 实例:CAS机制

    自旋锁

    • 概念:不放弃CPU事件,不断使用 CAS 尝试对数据进行更新,直到成功
    • 实例:AtomicInt 使用自旋锁,保证数据的原子性
    • 其实也是一种乐观锁

    可重入锁

    • 概念:同一个线程,在拿到一次锁之后,可以继续调用同一把锁(owner)进行同步的代码。
    • 实例:synchronized 就是可重入锁

    独享锁

    • 概念:给资源加上写锁,线程可以修改资源,其他线程不能再加锁
    • 实例:ReentrantReadWritelock 的 WriteLock

    共享锁

    • 概念:给资源加上读锁后只能读不能改,其他线程也只能加读锁,不能加写锁; (多读)
    • 实例:ReentrantReadWritelock 的 ReadLock

    公平锁、非公平锁

    • 概念:对资源的争抢是否有严格的先后顺序。在抢锁时,先尝试抢一下,再把自己加入等待队列。

     

    synchronized

    概念

    • 最基本的线程通信机制,是基于监视器对象(Monitor)实现的
    • 每个对象都会关联一个线程监视器,线程可以对他加锁/解锁
    • 一次只能有一个线程锁定监视器,当监视器被锁定,其他线程获取时会被阻塞。
    • 很重要:同步关键字保证了内存可见性

    特性

    • 可重入:当一个线程获取监视器后,可以多次调用临界区diamante
    • 独享:一次只能有一个线程锁定监视器
    • 悲观锁:直接获取锁

    范围

    • 类、对象

     

    锁消除/锁粗化

    • 属于运行时的 JIT 编译优化
      • 拓展:通过JitWatch查看经过JIT优化后的代码

    锁消除

    • 对于某些局部变量的代码,可能不会出现线程安全问题,那样锁就会被消除
    public void genStr(){
        //JIT 优化,消除了锁
        StringBuffer sb = new StringBuffer();
        sb.append("a");
        sb.append("b");
        sb.append("c");
    }
    • 优化前:出现了 Lock 相关的汇编指令(从左到右依次是:代码/Java指令/汇编指令)

    image.png

    • 优化后:没有了锁的存在

    image.png

    锁粗化

    • JIT基于性能考虑,可能会将锁的范围扩大
    public class LockDemo01{
        int count;
        public void runTest(){
             for(int i = 0; i < 10000; i++){
                //sych 会锁到 for 循环外面
                synchronized(this){
                    count++;   
                }
             }
        }
    }

     

    Synchronized加锁原理

    MarkWord    

    一个对象有三部分,MarkWord 就是其中的一部分。可以理解为对象头的一部分。

    同步关键字,其实就是操作对象的内存

    image.png

    • HotSpot中,对象前面会有一个类指针和标题,储标识哈希码的标题字以及用于分代垃圾收集的年龄和标记位
    • 默认情况下JVM锁会经历:偏向锁->轻量级锁->重量级锁这四个状态。
      • 偏向锁:TheadId
      • 轻量级锁:Lock Record Address,存储线程栈中 LockRecord 的地址
      • 重量级锁:Monitor Address,存储 monitor obj 的地址
        • 因为涉及到了 Monitor 对象,所以可以认为是重量级的
    • 相关文档:📎HotspotOverview.pdf

     

    锁状态的改变

    无锁->轻量级锁

     

    image.png

    • 概念
      • LockRecords:线程持有锁的信息,一个线程可能会持有多把锁,LockRecords 记录了当前拿到了哪些对象的锁
      • MarkWord:记录了当前锁定的线程栈的地址
    • 过程
      • 加锁:使用CAS修改 MarkWord 完毕,加锁成功。则 MarkWord 中的 tag 进入00状态。
      • 解锁:则是一个逆向恢复mark word的过程
    • 总结
      • 解锁和解锁,其实就是一个 CAS 修改内存(MarkWord/线程栈)的过程

     

    偏向锁 -> 轻量级锁

    image.png

    • 概念
      • 偏向标记:在出现争用后就失效了(未出现争用则不会失效)
        • -XX: -UseBiasedLocking 禁用使用偏置锁定
      • 偏向锁:本质就是无锁,如果没有发生过任何多线程争抢锁的情况,JVM认为就是单线程,无需做同步
        • 通过 ThreadId 去判断是否为当前线程,只是判断,不会进行 CAS 操作
    • 问题1:为什么有偏向锁
      • 同步在 JVM 底层是需要很多操作来实现,如果没有出现争用,就不需要进行同步操作
    • 问题2:线程重入是如何判定的
      • 因为在 MarkWord 中记录了当前持有锁的线程的 ThreadID,所以可以比对出,是否可以重入

     

    轻量级锁 -> 重量级锁

    image.png

    • 修改 MarkWord 如果失败,会自旋 CAS 一定次数(可以通过参数配置)
    • 超过次数,仍未抢到锁,则锁升级为重量级锁,进入阻塞。
    • Monitor 也叫做管程,计算机操作系统原理中有提及类似概念。一个对象会有一个对应的 Monitor。

     

    加锁流程总结

    重量级锁/轻量级锁/未锁定

    相关代码:src.share.vm.runtime.ObjectMonitor

    image.png

    1. 当 线程-1 请求持有锁时,如果对象时 01 (unlock),则会获得锁,并将对象头中的标志位置为 轻量级锁 (00)
      1. 线程是通过 CAS 自旋的方式去请求锁的
    2. 当对象状态位 00 时,有其他线程请求锁,线程首先通过 CAS 自旋的方式去尝试获得锁,当尝试达到次数没有获得时,对象的状态会变为 10.
      1. 这里不再使用自旋等待的原因是自旋会消耗大量资源
      2. 之所以是重量级,是因为要操作两个对象 > 原对象、monitor 对象
    3. 此时对象的 monitor 中,_owner 会变为 线程-1,新的请求线程会放到 _EntryList 中等待
      1. _EntryList 是争抢队列!!!
    4. 线程释放 _owner 会有两种方式
      1. 当持有锁的线程执行完后,会 monitorExit.
      2. 调用对象的 wait 方法后,会进入等待集合 _waiters
      3. 线程要进入 _waiters 队列,需要 _owner 线程调用 wait 方法,所以需要在同步代码块中执行 wait()/notify() 操作
      4. 因为等待集合是 Set,所以 notify 唤醒的时候不确定唤醒哪个

     

    偏向锁

    优先级: 未锁定 >>> 偏向锁 >>> 轻量级 >>> 重量级

    image.png

    • 轻量级锁的标志位存在 对象头 的 MarkWord 中,默认是开启的
    • 升级过程
      • 线程1 请求持有锁,会先检查偏向锁锁的标志位,如果为 1 打开,则通过 CAS 修改线程信息。
        • 线程信息中记录了当前获得偏向锁的线程的 ThreadId
      • 如果修改成功,就拿到了锁
      • 如果通过 CAS 修改失败,且线程状态是 01 未锁定,则升级到轻量级锁被清空标志位。(产生了锁竞争)
        • 如果此时已锁定,则要进行 CAS 争抢
    • 偏向锁释放,会清空线程信息
    • 为什么要用偏向锁
      • 因为 JVM 的设计理念认为,大多数情况下,并不存在锁竞争,不需要频繁修改标志位,减少了变为 重量级锁 的可能性。带来了性能提升。

     

    拓展:通过JitWatch查看经过JIT优化后的代码

    • 输出jit日志
      • windows:在 jre/bin/server 放置 hsdis 动态链接库
      • eclipse、idea等工具,加上JVM参数
    -server -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation -XX:LogFile=jit.log
    
    • 工具安装
    • 配置jitwatch
      • 页面选择 config, 配置要调试的项目src源码路径,和class编译路径
      • 打开jit.log
      • 点击start
    • 在分析的结果中,选中指定的类,再选择右侧的具体方法,则弹出jit编译结果
    展开全文
  • 这样会产生一个问题那就是怎么保证我检测冲突和操作能够具有连贯的原子性?总不能我冲突检测完成了,但是在我操作的时候其他线程已经把这个值改了。但如果我加锁的话,那不还是阻塞同步(悲观锁)了吗? 因此这个CAS...

    一、概念

    CAS(compare and swap/set)称为比较并交换或者自旋锁,是一种基于冲突检测的乐观锁,也称之非阻塞同步。换句话说就是不管三七二十一我先尝试操作,要是没有其他线程和我冲突那我就操作成功了,否则我就进行不断地重试直到没有冲突产生,期间不会去阻塞其他线程。

    这样会产生一个问题那就是怎么保证我检测冲突和操作能够具有连贯的原子性?总不能我冲突检测完成了,但是在我操作的时候其他线程已经把这个值改了。但如果我加锁的话,那不还是阻塞同步(悲观锁)了吗?

    因此这个CAS操作只能够通过硬件指令集来完成,在硬件的层面保障冲突检测和操作的原子性。在x86指令集中有一个cmpxchg来实现这个功能。正由于天然的硬件支持,CAS的效率在冲突不是非常剧烈的情况下会比悲观锁高很多,因为少了很多用户态内核态的切换以及线程挂起唤醒的开销。

    二、原子类

    JDK1.5之后,sun.misc.Unsafe类的出现使得Java类库中才开始使用cas操作(非安全类,应用层代码无法使用)。cas操作一般要三个参数:变量内存地址期待值新值。即在进行cas的时候cpu需要比较该内存地址变量的值是否为期待值,此阶段为冲突检测;如果一致那就说明没有冲突,然后将新值赋给该变量,此为交换阶段。

    JDK中最具代表性的CAS我觉得就是一系列原子类了。来看下AtomicInteger是如何实现自增操作的:

    // jdk:1.8, OS:MacOS
    
    // java.util.concurrent.atomic.AtomicInteger
    public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    
    // sun.misc.Unsafe
    public final int getAndAddInt(Object o, long valueOffset, int delta) {
            int expect;
            do {
                expect = this.getIntVolatile(o, valueOffset);
            } while(!this.compareAndSwapInt(o, valueOffset, expect, expect + delta));
            
            return expect;
    }
    

    可以看到是以CAS(atomicInteger的地址,当前值,当前值+1)实现的,若有冲突则不停重读当前值然后加一,直到成功为止。上述Unsafe.compareAndSwapInt方法是一个本地方法,在Hotspot源码中如下

    // hotspot/src/share/vm/prims/unsafe.cpp
    UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
      UnsafeWrapper("Unsafe_CompareAndSwapInt");
      oop p = JNIHandles::resolve(obj);
      jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
      return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
    UNSAFE_END
    
    // hotspot/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
    inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
      int mp = os::is_MP();
      __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
                        : "=a" (exchange_value)
                        : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                        : "cc", "memory");
      return exchange_value;
    }
    

    可以看出,在汇编码层面最终是以lock cmpxchgl实现的。

    三、ABA问题

    CAS操作存在一个逻辑漏洞,那就是读取的时候变量值为A,再进行CAS(V,A,N)的时候这个A值是有可能被修改过的,例如上面AtomicInteger中的自增expect = this.getIntVolatile(o, valueOffset);假设expect为0,当进行this.compareAndSwapInt(o, valueOffset, expect, expect + delta)的时候这个expect值有可能已经被+1然后又-1过了,因为期间可能发生了线程切换。ABA->010

    针对这个问题JDK提供AtomicStampedReference类来解决,它是通过控制版本形式来保障的。这个类在实例化的时候会要求存入一个引用和版本,然后CAS的时候会要求传入期望版本值和新版本值。不过根据《深入理解java虚拟机》的说法:这个类非常鸡肋,大部分情况下ABA问题不会影响程序并发的正确性,如果要解决ABA问题,使用传统的互斥同步会更高效。

    // java.util.concurrent.atomic.AtomicStampedReference
    public boolean compareAndSet(V   expectedReference,
                                     V   newReference,
                                     int expectedStamp,
                                     int newStamp) {
            Pair<V> current = pair;
            return
            	// 期望引用值需要等于当前引用值
                expectedReference == current.reference &&
                // 期望版本值需要等于当前版本值
                expectedStamp == current.stamp &&
                // 设置的是否还是原来的值或者新值是否设置成功
                ((newReference == current.reference &&
                  newStamp == current.stamp) ||
                 casPair(current, Pair.of(newReference, newStamp)));
    }
    

    四、参考

    深入理解java虚拟机

    展开全文
  • 非常局限的场景下,可以保证原子性 保证变量的内存可见性,被Volatile修饰的变量,每次读,必须从主内存同步;每次写,必须附带写回主内存 (最重要的作用-90%) 非常局限的场景下,可以保护代码重排引起的问题 ...

    Volatile
    只能保证操作的内存可见性和有序性,不能保证对变量的操作是原子性的。
    作用
    非常局限的场景下,可以保证原子性
    保证变量的内存可见性,被Volatile修饰的变量,每次读,必须从主内存同步;每次写,必须附带写回主内存 (最重要的作用-90%)
    非常局限的场景下,可以保护代码重排引起的问题

    在这里插入图片描述

    Synchronized :
    可以保证原子性、可见性和有序性。每个 Java 对象都有一个关联的 monitor,使用synchronized 时 JVM 会根据使用环境找到对象的 monitor,根据 monitor 的状态进行加解锁的判断。如果成功加锁就成为该 monitor 的唯一持有者,monitor 在被释放前不能再被其他线程获取。

    在这里插入图片描述

    Java语法:
    修饰方法,用在普通方法或者静态方法之前

    public synchronized void method() {
         
        }
    public static synchronized void staticMethod() {
              
        }
    
       2. 同步代码块
    
    public void someMethod() {
            // 同步代码块,用在方法内部
            Object o = new Object();
            synchronized (o) {
                // o 这个引用不能是 null
            }
        }
    

    在这里插入图片描述

    Synchronized是如何实现加锁的 - 以同步代码块为例

    如果Sync加锁失败,会怎么办?
    加锁失败,之后就没有资格继续执行代码了
    占据CPU没有意义了
    会触发线程调度 ,加锁失败的线程,会被调度器从CPU调度下来
    在锁打开之前,再分配CPU给该线程也没有意义
    线程状态要变化(不再是Runnable(ready/running))
    线程状态变成Blocked(sync加锁失败专用状态)
    当准备好了释放锁时,可以找到该线程,把它叫回来
    把线程加到这把锁的阻塞队列中(Blocking queue)

    Volatile 和 Synchronized 的区别
    Volatile本质是告诉JVM当前寄存器(工作内存)中的值是不确定的,需要从主内存中读取;Syn则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞。
    volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的。
    volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性
    volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞(多个线程争抢synchronized 锁对象时,会出现阻塞)
    volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。
    仅仅使用 volatile 并不能保证线程安全性。而 synchronized 则可实现线程的安全性。因为线程安全取决于原子性和可见性

    展开全文
  • 在涉及到多线程编程的时候,有些一些代码在执行的时候必须要保证它的原子性,例如当前int a=1。有个方法a()在执行a=a+1的时候,刚刚执行到把a=1取出来,存放到寄存器中,还没有来得及加,这个时候时间片用完了,于是...
  • AQS 全称是 AbstractQueuedSynchronizer,顾名思义,是一个用来构建锁和同步器的框架,它底层用了 CAS 技术来保证操作的原子性,同时运用了 CLH 同步队列作同步器,这也是 ReentrantLock、CountDownLatch 等同步工具...
  • volatile详细解读

    2019-09-17 17:38:57
    面试连环炮: ...volatile是java虚拟机提供的轻量级的同步机制,保证了可见性,有序性,不保证原子性 JMM(Java内存模型)是围绕着并发编程中原子性、可见性、有序性这三个特征来建立的,解释如下: 一、...
  • volatile关键字2.1 volatile保证原子性吗?2.2 volatile能保证有序性吗?2.3 volatile底层实现机制3. 正确使用volatile关键字3.1 状态标志3.2 双重检查模式 DCL4. 总结Ref 0. 前言 有时仅仅为了读写一个或者两个...
  • Effective java-并发 笔记

    2017-05-18 11:06:42
    不是,同步一方面意味着互斥访问,这保证同步操作的原子性,可以防止线程访问到对象的不一致状态;另一方面,同步意味着保证可见性,他保证进入同步方法或者同步代码快的每个线程,都看到由同一个锁保护的之前所有的修改...
  • volatile关键字

    2020-04-22 18:13:05
    volatile是java虚拟机提供的轻量级的同步机制,它保证可见性,不保证原子性,禁止指令重排。基本上遵守了jmm的规范(可见性,原子性,有序性) long和double的读写时原子的吗?如果使用Volatile修饰之后呢? lock ...
  • 1.请谈谈你对volatile的理解 1)volatile是JVM提供的轻量级的同步机制 三大特性: 保证可见性 不保证原子性 禁止指令重排序 2)你谈谈JMM 3)你在哪些地方用到过volatile 2.CAS你知道吗 3.
  • 视频地址:...1、 请谈谈你对volatile的理解 volatile是java虚拟机提供的轻量级的同步机制 保证可见性 不保证原子性 禁止指令重排 2、 CSA你知道吗? 3、 原子类 AtomicInteger 的 ...
  • 我叫目录2019年面试题1.请谈谈对volatile的理解CAS...1.1.2不保证原子性 1.1.3禁止指令重排 1.2JMM(Java内存模型Java Memory Model,简称JMM)本身是一种 抽象的概念并不真实存在,它描述的是一组规则或规范,通过这
  • Java并发编程

    2018-04-03 15:30:56
    保证在多个线程之间共享同一个全局变量或者静态变量,保证数据一致性、原子性。线程同步有哪些方式?synchronized、lock线程同步提高了效率吗?降低了程序效率、阻塞、抢锁的资源,效率并不高java并发包 java.util....
  • volatile是Java虚拟机提供的轻量级的同步机制,有3个特性,分别是:保证可见性、不保证原子性、禁止指令重排 2、什么是指令重排 计算机在执行程序时,为了提高性能,编译器在编译java代码和处理器jvm字节码的时候...
  • 文章目录思考题问:A线程正在执行一个对象的同步方法,B线程是否...问:写一个程序,证明Atomic原子类比synchronize更高效问:Atomic原子类可以保证可见性吗?请写一个程序来证明问:写一个程序证明Atomic原子类的...
  • 保证被volatile修饰的变脸对所有线程的可见。 第二个语义是禁止指令重排序优化 问题 Java 中能创建 volatile 数组吗? volatile 能使得一个非原子操作变成原子操作吗? volatile 修饰符的实际应用? ...
  • 答:Volatile关键字保证了内存的可见性,但不能保证原子性操作。面试官:那么你了解它的底层实现原理吗?答:不了解。(等通知吧....)背景知识:不可变对象对多线程有什么帮助? 不可变对象保证了对象的内存可见性...
  • volatile作用: 被volatile修饰的变量保证了可见性。...如果是将其他的变量 或者 加减乘除操作得到的结果赋值给变量,读取变量值以及计算的过程不是原子性的(可以看一下使用场景中的例子) 可见性:实现.
  • 其中2.volatile保证原子性吗?中的例子的问题,评论中15楼的回答个人感觉有道理,如下: 1、线程1读取inc的值后,还没有操作就被阻塞了。 2、线程2被唤醒,从主存读取inc的值,加1,然后被阻塞。(此时还没来得及把...
  • 互斥锁的实现

    2021-04-24 21:48:40
    所以我们就引入了锁来保证这个过程不出问题,但是互斥锁本身也是一个数值,系统根据互斥锁的值来决定是不是继续向下执行,那么为什么锁自身的修改就能保证原子性呢: 这是依赖于硬件,硬件原子指令法: //读者对CAS...
  • 为了解决多线程下线程对资源的读写发生混乱,例如不同线程读写发生不一致的情况,所以需要给这些资源加上锁,保证资源读写的原子性。 Synchronized 大致可以分为 2 类,一种是锁住对象,一种是锁住类: 1.锁对象...
  • java并发编程的艺术

    2020-07-29 11:54:49
    *2.1.3 volatile如何保证其在多处理器情景中的数据的可见?2.2 synchronized 的实现原理与应用2.2.2 锁的升级与对比偏向锁:为让线程获得锁的代价更低而引入了偏向锁。2.3 原子操作的实现原理2.3.2
  • 这包括脚本和事务的原子性保证。 由于KeyDB与Redis开发保持同步,因此KeyDB是Redis功能的超集,因此KeyDB可以取代现有Redis部署。 在相同的硬件上,KeyDB可以实现比Redis更高的吞吐量。 Active-Replication简化了...
  • 1、Spring Cloud来源于Spring,质量、稳定、持续都可以得到保证。 2、Spring Cloud天然支持Spring Boot,更加便于业务落地。 3、Spring Cloud发展非常的快,从16年开始接触的时候相关组件版本为1.x,到现在将要...
  • java并发编程

    2019-01-03 14:57:24
    第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器CopyOnWriteArrayList原理与使用00:15:52分钟 | 第48节并发容器ConcurrentLinkedQueue原理与使用00:31:03分钟 | 第49节Java中的阻塞队列原理与使用00:26:...
  • 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器CopyOnWriteArrayList原理与使用00:15:52分钟 | 第48节并发容器ConcurrentLinkedQueue原理与使用00:31:03分钟 | 第49节Java中的阻塞队列原理与使用00:26:...
  • 解密导致并发问题的第二个幕后黑手——原子性问题 解密导致并发问题的第三个幕后黑手——有序性问题 何为Happens-Before原则?这次彻底懂了! 如何解决可见性和有序性问题?这次彻底懂了! synchronized原理是什么?...

空空如也

空空如也

1 2
收藏数 39
精华内容 15
关键字:

同步保证原子性吗