CAS

阿里面试官:小伙子,你给我说一下JVM对象创建与内存分配机制吧

 ̄綄美尐妖づ 提交于 2020-07-24 00:39:58
内存分配机制 逐步分析 类加载检查: 虚拟机遇到一条new指令(new关键字、对象的克隆、对象的序列化等)时,会先去检查这个指令的参数在常量池中定位到一个类的符号引用,并且这个符号引用代表的类是否应被加载过,如果没有那么就去加载该类 分配内存 类加载完毕后会给对象分配内存空间。对象的所需的内存大小在类加载完毕后就便可完全确认,为对象分配内存大小的空间等同于把一块确定大小的内存从java堆中划分出来。 如何划分内存? 指针碰撞(默认使用指针碰撞):如果java堆内存是绝对规整的,那么会把所有用过的内存放在一边,空闲的内存放在另外一边,中间用一个指针来作为分界点的指示器,那所分配的内存仅仅把那个指针空闲空间的挪动一段与对象大小相同的距离。 空闲列表:如果java堆内存不是绝对规整的,已使用的空间和未使用的空间互相交错,那么虚拟机维护一份列表,记录哪些内存块是可用的,在划分内存空间的时候从列表中找到一块足够大的内存空间分配给对象实例,并更新列表上的记录。 分配内存遇到高并发的问题?现在有多个线程同时并发需要进行内存分配 CAS :虚拟机采用失败重试的机制方式保证操作的原子性对分配内存空间的动作进行同步处理,第一个线程抢占到了分配空间,第二个线程没有抢占到就重试抢占后面一块内存空间 本地线程分配缓冲:把内存分配的动作按照线程分配在不同的空间之中完成

Java中各种锁的详细解读

自闭症网瘾萝莉.ら 提交于 2020-05-09 20:28:42
在没有实际接触和详细了解Java的各种锁之前,可能觉得Java 中的各种锁,会很多很复杂,不是的,看一遍不行,再看一遍,就差不多了,还是比较好理解的。虽然距离实际使用还是有点距离,但是,要跨出第一步,了解之后,再考虑如何使用和高级使用。 这个图画的也很好 1. 乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度。在Java和数据库中都有此概念对应的实际应用。 先说概念。对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。 Java中,synchronized关键字和Lock的实现类都是悲观锁 。 而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。 乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。 (图还是很值得一看滴) 根据从上面的概念描述我们可以发现: 悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。 乐观锁适合读操作多的场景

Java 多线程-synchronized用法

柔情痞子 提交于 2020-05-08 18:31:20
造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。 当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式有个高尚的名称叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。 synchronized的三种应用方式 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 synchronized作用于实例方法 所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法,如下

2020 java 并发编程面试题及答案(最全版本持续更新)

不打扰是莪最后的温柔 提交于 2020-05-08 15:54:11
前言 涵盖各大公司会问到的面试点,同时随着版本的升级,可能也会有一些面试题更新,也会同步保持更新,因为篇幅原因(其实是我懒,哈哈)所以列了一部分答案,所有的答案见下文,总共485页合计20个技术点,文末自取pdf. 1、在 java 中守护线程和本地线程区别? java 中的线程分为两种:守护线程(Daemon)和用户线程(User)。 任何线程都可以设置为守护线程和用户线程,通过方法 Thread.setDaemon(boolon);true 则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在 Thread.start()之前调用,否则运行时会抛出异常。 两者的区别: 唯一的区别是判断虚拟机(JVM)何时离开,Daemon 是为其他线程提供服务,如果全部的 User Thread 已经撤离,Daemon 没有可服务的线程,JVM 撤离。也可以理解为守护线程是 JVM 自动创建的线程(但不一定),用户线程是程序创建的线程;比如 JVM 的垃圾回收线 程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是 Java 虚拟机上仅剩的线程时,Java 虚拟机会自动离开。 扩展:Thread Dump 打印出来的线程信息,含有 daemon 字样的线程即为守护 进程,可能会有:服务守护进程、编译守护进程

Java 中的 CAS 简述及原理解析

℡╲_俬逩灬. 提交于 2020-05-08 10:32:30
一、CAS 是什么? CAS(Compare And Swap),比较并交换,它是一条CPU并发原语。它的功能是判断内存某个位置的值是否为预期值,如果是则更新为新的值,这个过程是原子的。 1 public class CASDemo { 2 public static void main(String[] args) { 3 AtomicInteger atomicInteger = new AtomicInteger(5); 4 5 System.out.print(atomicInteger.compareAndSet(5, 2019)); 6 System.out.println(" the value are " + atomicInteger.get()); 7 8 System.out.print(atomicInteger.compareAndSet(5, 1024)); 9 System.out.println(" the value are " + atomicInteger.get()); 10 } 11 } CAS 并发原语体现在 Java 语言中就是 sun.misc.Unsafe 类中的各个方法。调用 Unsafe 类中的 CAS 方法,JVM 会帮我们实现 CAS 汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作,再次强调,由于CAS

廖雪峰Java11多线程编程-3高级concurrent包-5Atomic

一世执手 提交于 2020-05-08 08:03:39
1. Atomic java.util.concurrent.atomic提供了一组原子类型操作。如AtomicInteger提供了 int addAndGet(int delta) int incrementAndGet() int get() int compareAndGet() 1.1 无锁线程安全 Atomic类可以实现<font color="blue">无锁(lock-free)的线程安全(thread-safe)访问</font> <p style="color: red;background-color: yellow">原理:CAS(Compare and Set)如果AtomicInteger实例的值是prev,就替换为next,返回true;否则,返回false</p> ```#java /* * 在这个操作中,如果AtomicInteger当前的值是prev,就更新为next,返回true,终止循环; * 如果AtomicInteger当前的值不等于prev,就什么也不做,返回false,继续循环。 */ public int add1AndGet(AtomicInteger var){ int prev, next; do{ prev = var.get(); //之后其他的线程修改了prev的值,也不影响结果 next = prev + 1;

Java并发(十八):阻塞队列BlockingQueue

╄→гoц情女王★ 提交于 2020-05-08 05:50:55
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。 阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。 阻塞队列提供了四种处理方法: 方法\处理方式 抛出异常 返回特殊值 一直阻塞 超时退出 插入方法 add(e) offer(e) put(e) offer(e,time,unit) 移除方法 remove() poll() take() poll(time,unit) 检查方法 element() peek() 不可用 不可用 对于 BlockingQueue,我们的关注点应该在 put(e) 和 take() 这两个方法,因为这两个方法是带阻塞的。 抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。 返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素

Java运行时环境---内存划分

若如初见. 提交于 2020-05-08 04:09:27
背景 :听说Java运行时环境的内存划分是挺进BAT的必经之路。 内存划分 : Java程序内存的划分是交由JVM执行的,而不像C语言那样需要程序员自己买单(C语言需要程序员为每一个new操作去配对delete/free代码),放权给JVM虚拟机处理有利也有弊,好处是不容易出现内存泄漏和内存溢出问题,坏处就是自己的屁股不能自己擦,万一有一天JVM罢工不释放了,还是自个忘了释放,So了解虚拟机容易引起内存泄漏和溢出的场景对Java程序员来说还是必不可少的。【内存溢出:Out Of Memmory,系统已经不能再分配空间了,好比你需要50M的空间,系统就只剩下40M;内存泄露:Memmory Leak,开辟了资源空间但用完后忘记释放,内存还在被占用,多次内存泄漏就会导致内存溢出;】了解JVM内存划分要端到端,先从Java程序执行的具体过程来看: 从图1中可以清楚看到Java程序的执行过程,大致就是Java源代码(后缀为.java)首先被Java编译器编译成字节码文件(后缀为.class),然后交由JVM中的类加载器加载各个类的字节码文件,加载好字节码文件后再交由JVM引擎执行。在整个程序执行过程中,上图中运行时区域会用一段空间来存储程序执行期间需要用到的数据和相关信息,也就是我们弄懂内存划分要深度研究的区域,即JVM。 运行时数据(内存)区: 图2中

【数据结构】线段数/segment tree/interval tree

谁说我不能喝 提交于 2020-05-08 03:30:10
【线段树】   本质是二叉树,每个节点表示一个区间 [ L, R ] ,设 m = (R - L + 1) / 2 (该处结果向下取整) 左孩子区间为 [ L, m ] , 右孩子区间为 [ m+1 , R ]    同时每个节点(即每个区间)维护一个信息 (该信息能通过子节点区间结果 合并 得到父区间结果)    图解:   例如对于数组[2, 5, 1, 4, 9, 3]可以构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间arr[0...5]内的最小值是1):   区间信息:该区间上的最小值    【应用】   动态RMQ      【模板】   点修改模板参见例题hdu1754   区间修改模板( 此处有个优化:延迟标记 )    【例题】   hdu1754 /* hdu1754 找区间最大值 */ #include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int INF = - 100 ; const int maxn = 2000100 ; int segtree[maxn]; inline void pushup( int root){ segtree[root] = max( segtree

[Java复习] 面试突击

落爺英雄遲暮 提交于 2020-05-07 20:04:09
synchronized关键字的底层原理? 用于线程同步,加锁。 可用于类,对象,块。一般是对一个对象进行加锁。 // 线程1 synchronized (myObject) { -> 类的class对象来走的 // 一大堆的代码 synchronized (myObject) { // 一大堆的代码 } } synchronize底层原理与JVM指令和monitor有关系。深入涉及CPU硬件原理,原则性、可见性、有序性、指令重排、偏向锁、JDK的对其进行的优化。 synchronized关键字,底层编译后的JVM指令中,使用 monitorenter 和 monitorexit 指令。 monitorenter:加锁 monitorexit:解锁 如果一个线程第一次synchronized那里,获取到了myObject对象的monitor的锁,计数器加1,然后第二次synchronized那里,会再次获取myObject对象的monitor的锁,这个就是重入加锁了,然后计数器会再次加1,变成2。 这个时候,其他的线程在第一次synchronized那里,会发现说myObject对象的monitor锁的计数器是大于0的,意味着被别人加锁了,然后此时线程就会进入block阻塞状态,什么都干不了,就是等着获取锁。 接着如果出了synchronized修饰的代码片段的范围