Java并发
并发容器
ConcurrentHashMap原理
hashtable在整个hash表的put操作上加上synchronized锁,效率很低。
ThreadLocal
重点在于ThreadLocal的底层数据结构和操作,ThreadLocal为什么会发生内存泄漏,以及ThreadLocal用在什么样的场景
ThreadLocal使用的是一种“空间换时间的思路”,使每个线程对于临界资源都有自己的本地变量,每个线程都会都拥有自己的“共享资源”无疑内存会大很多,但是由于不需要同步也就减少了线程可能存在的阻塞等待的情况从而提高的时间效率。
ThreadLocal有set、get、remove三种常见操作,要知道三种常见操作的过程。
ThreadLocal是如何存储的,以及ThreadLocal为什么会有内存泄露的问题。
锁机制
Lock和Synchronized的区别
reentrantLock原理
AQS
AbstractQueuedSynchronizer抽象类是核心,需要重点掌握。它提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架。
AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
关键字
volatile
一个直观的认知是,被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。
volatile的实现,说实话不太懂
synchronized原理
(1)概念
synchronized是Java中常用的锁之一,是一个并发控制的关键字,直观来说就是控制多线程并发访问共享资源时的同步,保证同一时刻只有一个线程能够访问共享资源。
synchronized主要有三种用法:修饰实例方法,相当于给当前对象(实例)加锁;修饰静态方法,锁住的是当前class(类)??修饰代码块:synchronized(this)是锁当前实例,synchronized(类.class)是锁当前class。
保证原子性:被synchronized修饰的类或对象的所有操作都是原子的,因为在执行操作之前必须先获得类或对象的锁,直到执行完才能释放。
保证可见性:ynchronized和volatile都具有可见性,其中synchronized对一个类或对象加锁时,一个线程如果要访问该类或对象必须先获得它的锁,而这个锁的状态对于其他任何线程都是可见的,并且在释放锁之前会将对变量的修改刷新到共享内存当中,保证资源变量的可见性。
保证顺序性:synchronized和volatile都具有有序性,Java允许编译器和处理器对指令进行重排,但是指令重排并不会影响单线程的顺序,它影响的是多线程并发执行的顺序性。synchronized保证了每个时刻都只有一个线程访问同步代码块,也就确定了线程执行同步代码块是分先后顺序的,保证了有序性。
(2)实现
加锁和释放锁的原理:monitorenter和monitorexit的指令
可重入的原理:加锁次数计数器,monitor计数器
保证可见性原理:Synchronized的happens-before规则,即监视器锁规则:对同一个监视器的解锁,happens-before于对该监视器的加锁。
(3)变化
synchronized中锁的优化:无锁时当然是最快的
线程池
并发底座
并发理论JMM
出现并发问题的根源:可见性: CPU缓存引起;原子性: 分时复用引起;有序性: 重排序引起
JMM(Java内存模型)是用于解决Java并发问题的一套内存使用规范。JMM如何解决Java并发问题的:提供- volatile、synchronized 和 final 三个关键字和appens-Before 规则,来应对上面三性造成的并发问题。
并发安全具体实现方法(方式):1、互斥同步,synchronized和reentrantLock。2、非阻塞同步,CAS(基于冲突检测的乐观并发策略)和AtomicInteger原子类,Unsafe类的CAS操作??,ABA问题。3、无同步方案:ThreadLocal线程本地存储,用于线程间不需要同步的地方。
//todo这还不够
创建线程
这篇比较通俗易懂 segmentfault.com/a/119000003…
从形式上来说,创建线程又四种方式:除去继承Thread线程类、实现runnable接口、使用线程池三种方式之外,还有就是实现callable接口且结合Future接口实现。
关于callable和futrue以及两者结合FutureTask可以看下这个 www.cnblogs.com/guanbin-529…
关于线程相关的方法:interrupted(), join(), sleep()/wait(), yield() 可以看 github.com/CL0610/Java…
CAS、Unsafe和原子类,以及LockSupport
AQS框架借助于两个类:Unsafe(提供CAS操作)和LockSupport(提供park/unpark操作)。对AQS来说这就是俩核心类,而Lock实现则是用的AQS。
JVM
内存
垃圾回收GC
这篇应付下背八股的面试还可以 zhuanlan.zhihu.com/p/96404311
(一)判断一个对象是否可回收
(1)引用计数法:给对象加一个引用计数器,可能出现两个对象循环引用。
(2)可达性分析:GC Roots作为起点进行搜索,不可达的回收。
- 虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中的常量引用的对象
(二)引用
由上面判断是否可回收的地方可知,对象是否可回收都和引用是密切相关的。
强引用:new 一个对象时指向的引用,Object obj = new Object();
还在强引用中的对象不会被回收!!
软引用:SoftReference类创建软引用。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; // 使对象只被软引用关联
还在软引用中的对象,只有在内存不够的情况下才会被回收!!
弱引用:使用 WeakReference 类来实现弱引用。
还在弱引用中的对象,一定会在下一次垃圾回收时被回收!!
(三)垃圾回收算法
标记-清除
标记-整理
存活对象往内存空间的一边整理聚集起来。
标记-复制
将内存划为大小相等的两块,存活的对象复制到另一块整齐排列。
分代算法
一般将堆内存分为新生代和老年代。老年代用标记-清除 或 标记-整理算法。
新生代用标记复制算法
Eden区,from survivor区,to survivor区。
Minor GC 和 Full GC
major GC是指只回收老年代。
Eden区空间不够时,发起Minor GC。对象每经过一次Minor GC的回收后,年龄增加一岁,增加到一个参数后,就会进入老年代。大对象(字符串或数组)直接进入老年代。Minor GC前要判断老年代里面是否有充足空间,装下目前所有新生代的对象(空间分配担保)。
Full GC的触发条件要复杂一些,如老年代空间不足、空间分配担保失败、JDK1.7之前的元空间是要用老年代空间的等等情况。
垃圾回收器
(一)CMS收集器
CMS收集器是一个老年代收集器,垃圾手机分为4个步骤:
(二)G1收集器
G1把堆划分成多个大小相等的区域region,每个小空间可以单独进行垃圾回收。
重点关注特点、回收过程即可。
JVM内存区域(运行时数据区)
1、程序计数器:PC 寄存器用来存储指向下一条指令的地址,即将要执行的指令代码。所以线程私有
2、栈区:主要是虚拟机栈。存储 Java 程序运行需要的数据,它保存方法的局部变量、部分结果,并参与方法的调用和返回。所以线程私有
3、堆区:虚拟机管理的内存中最大的一块,被所有线程共享。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数据都在这里分配内存。
内存泄漏、溢出
逃逸分析
//todo
4、方法区:1.8后是元空间
//todo
类加载机制
类加载包括五个阶段:
加载:查找并加载类的二进制数据
连接-验证:确保被加载的类是正确的
验证程序的语义是合法的,build的时候能build起来,不会爆红之类的
连接-准备:为类变量(静态变量)分配内存,并将其初始化为默认值
这里的内存是在方法区,后面的元数据区分配的。而且仅包括类变量而不包括实例变量!
连接-解析:把类中的符号引用转换为直接引用,有一个符号变成一个指针(有的情况可能在初始化之后才开始,是为了支持Java的动态绑定)
初始化:主要为类的静态变量赋予正确的初始值,JVM负责对类进行初始化
声明类变量是指定初始值、或使用静态代码块为类变量指定初始值
类加载机制-双亲委派机制:
这篇10个问题,绝对双亲委派问题都有:zhuanlan.zhihu.com/p/343563937
下图展示的是组合关系,而不是extends基础关系。
过程:
优势:
确保类只被加载一次,避免重复加载;
此外,若用户有定义和系统类相同路径的类,最终被加载的是系统类而不是自定义的类,保证系统安全运行。
Java内存模型JMM
//todo
Java内存模型JMM(早期笔记)
首先需要明确的一点是,Java内存模型不是JVM的内存分区,两者是不同的概念!!
Java内存模型(Java Memory Model,JMM)是一种Java虚拟机规范。传统计算机体系中的内存模型,CPU+Cache+Memory在多核(每个核有自己的cache)的情况下,会有缓存cache(同一份数据)不一致的情况。内存模型则是在物理机器上定义出一套内存模型, 规范内存的读写操作。
Java内存模型是一套规范,具体看下面这个吧,按照这篇文章的顺序来说:
字节码文件.class
字节码增强技术
字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。