volatile

java多线程之volatile讲解

匿名 (未验证) 提交于 2019-12-02 21:53:52
可见性:volatile在多线程中能够保证共享变量的“可见性”,简单的说就是当一个线程修改了volatile变量的时候,java线程内存模型能够确保所有的线程看到的这个变量的值是一致的。 防止指令重排序 在学习volatile的知识之前我们先来简单了解下java内存模型(JMM)引用一张网上很经典的表示java内存模型的图 大概解释下这个图的意思 在多个线程运行的时候每一个线程(Thread)都有一个属于自己的内存空间 多个线程共同使用一个主存 每个线程在对数据进行修改之前都会先在主存里面获取相关数据,然后在自己的工作内存里面对数据进行操作。 关键的地方就来了,因为每个线程然都是在自己的内存里进行操作,然而每个线程的工作内存之间都是相互不可见的,所以对共享变量的修改并不会马上被其他线程看到,所以就会造成多个线程操作同一个数据但是最后结果并不是我们期望的结果。当线程1去首先从主存中加载一个volatile变量到自己的工作内,然后对这个volatile变量进行写操作,写入操作结束之后,volatile变量的最新值会立马刷新到主内存,同时其他线程中的这个volatile变量会立马失效,会被强迫从主内存中重新读取volatile变量的最新值,这就是volatile变量的可见性实现的过程。同时这也可以看做是一个线程和其他线程通信的一个过程。 简单解释下指令重排序

Java面试官最爱问的volatile关键字

匿名 (未验证) 提交于 2019-12-02 21:53:32
在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题。经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用volatile关键字作为切入点呢? 爱问volatile关键字的面试官,大多数情况下都是有一定功底的,因为volatile作为切入点,往底层走可以切入Java内存模型(JMM),往并发方向走又可接切入Java并发编程,当然,再深入追究,JVM的底层操作、字节码的操作、单例都可以牵扯出来。 所以说懂的人提问题都是有门道的。那么,先整体来看看volatile关键字都设计到哪些点:内存可见性(JMM特性)、原子性(JMM特性)、禁止指令重排、线程并发、与synchronized的区别……再往深层次挖,可能就涉及到字节码、JVM等。 不过值得庆幸的是,如果你已经学习了微信公众号“程序新视界”JVM系列的文章,上面的知识点已经不是什么问题了,权当是复习了。那么,下面就以面试官提问的形式,在不看答案的情况下,尝试回答,看看学习效果如何。夺命连环问,开始…… 被volatile修饰的共享变量,就具有了以下两点特性: 保证了不同线程对该变量操作的内存可见性; 禁止指令重排序; 回答的很好,点出了volatile关键字两大特性。针对该两大特性继续深入。 该问题涉及到Java内存模型(JVM

(转)大厂常问到的14个Java面试题

匿名 (未验证) 提交于 2019-12-02 21:52:03
1. synchronized和reentrantlock异同 相同点 都实现了多线程同步和内存可见性语义 都是可重入锁 不同点 实现机制不同 synchronized通过java对象头锁标记和Monitor对象实现 reentrantlock通过CAS、ASQ(AbstractQueuedSynchronizer)和locksupport(用于阻塞和解除阻塞)实现 synchronized依赖jvm内存模型保证包含共享变量的多线程内存可见性 reentrantlock通过ASQ的volatile state保证包含共享变量的多线程内存可见性 使用方式不同 synchronized可以修饰实例方法(锁住实例对象)、静态方法(锁住类对象)、代码块(显示指定锁对象) reentrantlock显示调用trylock()/lock()方法,需要在finally块中释放锁 功能丰富程度不同 reentrantlock提供有限时间等候锁(设置过期时间)、可中断锁(lockInterruptibly)、condition(提供await、signal等方法)等丰富语义 reentrantlock提供公平锁和非公平锁实现 synchronized不可设置等待时间、不可被中断(interrupted) 2. concurrenthashmap为何读不用加锁 jdk1.7 1

java 理解CAS

匿名 (未验证) 提交于 2019-12-02 21:52:03
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁(后面的章节还会谈到锁)。 锁机制存在以下问题: (1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。 (2)一个线程持有锁会导致其它所有需要此锁的线程挂起。 (3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。 volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。 独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。 CAS 操作 上面的乐观锁用到的机制就是CAS,Compare and Swap。 CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。 非阻塞算法 (nonblocking algorithms) 一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。 现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。

Java锁的实现

匿名 (未验证) 提交于 2019-12-02 21:52:03
volatile属性:可见性、保证有序性、不保证原子性。   Java的内存中所有的对象都存在主内存中,每个线程都有自己的栈和程序计数器,多个线程对同一个对象的变量读取时,会将对象的变量从主内存中拷贝到自己的栈帧里(操作数栈),线程之间也无法直接访问对方的操作数栈,只能通过主内存传递变量的值;    可见性: 如果对声明了volatile变量进行写操作时,JVM会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写会到系统内存。 这一步确保了如果有其他线程对声明了volatile变量进行修改,则立即更新主内存中数据。    有序性: 但这时候其他处理器的缓存还是旧的,所以在多处理器环境下,为了保证各个处理器缓存一致,每个处理会通过嗅探在总线上传播的数据来检查 自己的缓存是否过期,当处理器发现自己缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作时,会强制重新从系统内存把数据读到处理器缓存里。 这一步确保了其他线程获得的声明了volatile变量都是从主内存中获取最新的。 使用场景:状态标记,后续会再写另一个使用场景,单例模式下的volatile确保单例对象的返回的正确性。 原子操作:可以是一个步骤,也可以是多个步骤,但是其顺序不可以被打断,也不可以被切面只执行其中的一部分(不可中断性)。 并发示例: public

Java并发编程的艺术(六)happens-before

匿名 (未验证) 提交于 2019-12-02 21:52:03
1、happens-before是JMM最核心的概念,JSR-133使用happens-before来指定 两个操作之间的执行顺序 。 2、如果A happens-before B,那么: (1)对程序员来说:A的执行结果对B可见,并且A的执行顺序排在B之前。 (2)对编译器和处理器来说:不一定要按照程序顺序执行,在不改变程序执行结果(指 单线程 或 正确同步的多线程 )的前提下,允许重排序。 3、happens-before规则: (1) 程序顺序 规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。 (2) 监视器 规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。 (3) volatile变量 规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。 (4) 传递性 :如果A happens-before B,且B happens-before C,那么A happens-before C。 (5)start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。 (6)join()规则:如果线程A执行操作ThreadB.join()并成功返回

阿里java开发手册阅读心得

匿名 (未验证) 提交于 2019-12-02 21:52:03
最近抽空学习了下阿里之前开放出来的java开发手册,其中提出的规约有的是基于代码书写规范的统一,有的是基于代码的安全性、执行效率考虑的。其中提到的大部分要求在实际编码中都是很好的建议,也有个别建议在实际项目中可能并不合适,要视项目的规模、场景决定。这里总结了其中感觉比较有价值的一些规范: 1.所有的覆写方法,必须加@Override注解 这一点自己还是很有感触的,在很多项目代码中经常看到覆写父类的方法上不带该注解,通常情况下编译也不会报错。加上该注解就是明确告知代码阅读者该方法是多态的,同时提供了编译期检查,如果继承族上无该方法签名或方法签名被改动,编译器会报错。 2.避免通过一个类的对象引用访问此类的静态变量或静态方法 通过对象访问静态变量或静态方法会增加编译器解析成本,应该直接用类名来访问。在追求性能的代码中需要注意这一点 3.使用常量或确定有值的对象来调用equals NPE问题是java中最常见的问题,凡是涉及NPE问题的地方都需要注意。如果用null对象调用equals,会报空指针异常。所以好的习惯是使用"test".equals(object),而非object.equals("test")。如果觉得别扭,JDK7之后的版本可以使用Objects.equals(a,b) 4.使用索引访问用String的split方法得到的数组时,需做最后一个分隔符后有无内容的检查

How to cast away the volatile-ness?

瘦欲@ 提交于 2019-12-02 21:48:31
How to cast away the volatile-ness? Which c++ style cast should I use? Use const_cast . For example, volatile sample *pvs = new sample(); sample *ps = const_cast<sample*>(pvs); //casting away the volatile-ness That is, const_cast is used to cast away both const-ness as well as volatile-ness. Unfortunately, its name doesn't contain the term "volatile". Maybe, that is because the keyword const is more common in use than the keyword volatile . As one of the comment says, cv_cast would have been more appropriate name! 来源: https://stackoverflow.com/questions/5249895/how-to-cast-away-the-volatile

Java线程优先级

匿名 (未验证) 提交于 2019-12-02 21:45:52
线程优先级被线程调度用来判定何时每个线程允许运行。理论上,优先级高的线程比优先级低的线程获得更多的CPU时间。实际上,线程获得的CPU时间通常由包括优先级在内的多个因素决定(例如,一个实行多任务处理的操作系统如何更有效的利用CPU时间)。 一个优先级高的线程自然比优先级低的线程优先。举例来说,当低优先级线程正在运行,而一个高优先级的线程被恢复(例如从沉睡中或等待I/O中),它将抢占低优先级线程所使用的CPU。 理论上,等优先级线程有同等的权利使用CPU。但你必须小心了。记住,Java是被设计成能在很多环境下工作的。一些环境下实现多任务处理从本质上与其他环境不同。为安全起见,等优先级线程偶尔也受控制。这保证了所有线程在无优先级的操作系统下都有机会运行。实际上,在无优先级的环境下,多数线程仍然有机会运行,因为很多线程不可避免的会遭遇阻塞,例如等待输入输出。遇到这种情形,阻塞的线程挂起,其他线程运行。 但是如果你希望多线程执行的顺利的话,最好不要采用这种方法。同样,有些类型的任务是占CPU的。对于这些支配CPU类型的线程,有时你希望能够支配它们,以便使其他线程可以运行。 设置线程的优先级,用setPriority()方法,该方法也是Tread 的成员。它的通常形式为: final void setPriority(int level) 这 里 , level 指

Java同步的三种实现方式

匿名 (未验证) 提交于 2019-12-02 21:45:52
1.使用synchronized关键字修饰类或者代码块; 2.使用Volatile关键字修饰变量; 3.在类中加入重入锁 举例子:多个线程在处理一个 共享变量 的时候,就会出现线程安全问题。(相当于多个窗口卖票的操作) 非同步状态下: public static void main(String[] args){ Increase increase = new Increase(); int count = 10; while (count != 0){ new Thread(() -> { increase.increasementAndPrint(); }).start(); count --; } } static class Increase { private int i = 0; void increasementAndPrint() { System.out.print(i++ + "\n"); } } 这种情况下可能会导致多个线程输出的i相同: 0 0 1 2 3 4 5 6 7 8 使用同步: 1.使用synchronized关键字 //保证原子性和有序性 static class Increase { private int i = 0; synchronized void increasementAndPrint() { System.out.println