本篇博客内容,主要是我看了《java核心知识》后觉得有的内容有必要记下来,方便自己以后复习。
1.jdk1.8永久代变成了元数据区,类的元数据存在本地内存,字符串常量池和常量存在了堆中(jdk1.8以前都是存在方法区中)
2.程序计数器是唯一不会出现oom的;Eden:servivorFrom:servivorTo=8:1:1; 新生代:老年代=1:3(默认比值);minorGC=复制-》清空—》互换; cms收集器(老年代 多线程标记清除)有内存碎片,有游离数据问题;步骤是初始标记-并发标记-重新标记-并发清除 G1(新生代老年代都行,标记整理) 将堆分区,维护一个优先级列表,提高垃圾回收的效率
3.阻塞i0:一个请求,处理时一直阻塞,会让出cpu 非阻塞io:不会阻塞,但是会一直轮询发送请求,不会让出cpu 多路复用io模型(NIO,同步非阻塞):用selector轮询所有channel,直到有真正的读写请求时再用线程处理,读写都是通过buffer,线程通常将非阻塞io的空闲时间用来执行其他通道的io操作,基于事件驱动,epoll模型。 普通io和nio的最大区别是:一个面向流,一个面向缓存区
4.Thread start方法是一个native方法 Thread thread = new Thread(myThread); Thread.start();//start调用的就是目标类的run方法 Executor是线程池的顶级接口。 ExecutorService 线程启动后,由于不可能一只占着cpu,cpu会在线程之间切换,所有线程状态也会在运行阻塞之间切换
5.线程状态: 新建-使用new创建一个线程,jvm为其分配内存,初始化成员变量 就绪-调用了start方法,jvm创建方法调用栈和程序计数器,等待调度 运行-线程获得了cpu,开始执行run方法体 阻塞-因为某个原因放弃了cpu使用权,直到线程进入可运行状态,才有机会再次获得cpu转到运行状态 等待阻塞(wait)jvm将线程放入等待队列 同步阻塞(lock)若同步锁被别的线程占用,jvm会把线程放入锁池中 其他阻塞(sleep/join)把线程状态改为阻塞状态,后把线程重新转入可运行状态 线程处于阻塞状态:使用interrupt()方法中断线程时,一定需要先捕获InterruptedException异常后通过break来跳出循环,才能正常结束run方法。 线程未处于阻塞状态:使用 isInterrupted()判断线程的中断标志来退出循环 不推荐直接使用stop()直接结束线程
6.sleep与wait区别: 1).sleep是thread类的,wait方法是object类的; 2).Sleep和wait都会让出cpu,但是sleep监控状态依然保持,当指定的时间到了又会自动恢复运行状态; 3).在调用 sleep()方法的过程中,线程不会释放对象锁,而当调用 wait()方法的时候,线程会放弃对象锁进入等待池,wait会放弃所有锁并需要notify/notifyAll后重新获取到对象锁资源后才能继续执行。 4).sleep每个地方都可以使用,而wait只能在同步方法或者同步块中使用。
7.Synchronized: 核心组件: Wait set 存放调用了wait方法被阻塞的线程 Contention List,所有请求锁的线程首先被放在这个竞争队列中 entry list:Contenttion list中那些有资格成为候选资源线程被移动到entry list; onDeck:任意时刻,最多只有一个线程正在竞争锁资源, Owner:当前已经获得锁资源的线程 !Owner:当前释放锁的线程 每个对象都有个monitor对象,加锁就是竞争monitor对象,代码块加锁就是在前后加上monitorenter和monitorexit实现 方法加锁就是通过一个标志位来判断(acc_synchorized)
8.Lock ReentrantLock-可重入锁 Wait 需要用在同步代码块中,会释放对象锁 Sleep 不会释放对象锁 yield 使当前线程让出cpu执行时间片,与其他线程一起重新竞争cpu时间片 Interrupt 主要是影响线程内部的一个中断标示位,并不会中断一个正在运行的线程。 Join 等到其他线程终止 比如在main中调用了thread1.join(),这时main线程就转为了阻塞,等到thread1结束后继续执行 Notify 唤醒在此对象监视器上等待的单个对象
9.Pcb 上下文切换是内核在cpu上对于进程(线程)进行切换,切换过程中的信息保存在进程控制块(pcb)中
10.线程池:线程复用,控制最大并发数,管理线程,处理过程中将任务放入队列 线程池组成: 线程池管理器:创建并管理线程池 工作线程:线程池中的线程 任务接口:每个任务必须实现的接口,用于工作线程调度其运行 任务对列:用于存放待处理的任务,提供一种缓存机制
11.阻塞对列:add remove 抛异常(当阻塞队列没内容时,不同的api不同的处理方式) offer poll 特殊值 put take 阻塞 (reentrantLock 加wait实现) ArrayBlockingQueue(公平,非公平) LinkedBlocingQueue(其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,默认无限大小) PriorityBlockingQueue(compareTo 排序实现优先) DelayQueue(缓存失效、定时任务 ) SynchronousQueue(不存储数据、可用于传递数据)是一个不存储元素的阻塞队列。每一个 put 操作必须等待一个 take 操作,否则不能继续添加元素 LinkedBlockingDeque: 是一个由链表结构组成的双向阻塞队列
12.CountDownLatch 是不能够重用的,而 CyclicBarrier 是可以重用的 volatile 关键字的作用(变量可见性、禁止重排序),采用内存屏障 被volatile修改的变量值修改时(嗅探),会使其他线程的本地值失效,强制从内存重新读取 Java 内存模型(JMM)解决了可见性和有序性的问题,而锁解决了原子性
13.lock和synchronized 相同点:都是用来协调多线程对共享对象、变量的访问;可重入锁;都保证了可见性和互斥性 不同:lock是显示获得锁;可中断,更灵活;需要手动获得锁释放锁,synchronized可以自动释放锁 ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的 synchronized 是同步阻塞,使用的是悲观并发策略,lock 是同步非阻塞,采用的是乐观并发策略
14.ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成,Segment 是一种可重入ReentrantLock 减小锁粒度 Segment 的大小也被称为 ConcurrentHashMap 的并发度。 分段锁 默认情况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度 每个 Segment 守护一个 HashEntry 数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得它对应的 Segment 锁。
15.抢占式调度:每条线程执行的时间、线程的切换都由系统控制,一个线程的堵塞不会导致整个进程堵塞。 协同式调度:某一线程执行完后主动通知系统切换到另一线程上执行,不存在多线程同步问题,一个线程有问题可能导致整个系统崩溃 Jvm 使用的线程调使用抢占式调度,Java 中线程会按优先级分配 CPU 时间片运行,且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片 进程调度算法: 先来先服务调度算法(FCFS) 短作业(进程)优先调度算法
16.CAS(Compare And Swap/Set)比较并交换 CAS(V,E,N)当且仅当 V 值等于 E 值时,才会将 V 的值设为 N CAS 操作是抱着乐观的态度进行的(乐观锁) 原子包 java.util.concurrent.atomic(cas+锁自旋),cas调用的是jni(unsafe.compareAndSwapInt) CAS 会导致“ABA 问题”(通过添加版本号)
17.AQS(抽象的队列同步器):AQS 定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它 它维护了一个 volatile int state(代表共享资源)和一个 FIFO 线程等待队列(多线程争抢资源被阻塞时会存放进队列) AQS 定义两种资源共享方式(独占锁和共享锁) AQS 只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现 自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS 已经在顶层实现好了
18.同步器的实现是 ABS 核心(state 资源状态计数) 同步器的实现是 ABS 核心,以 ReentrantLock 为例,state 初始化为 0,表示未锁定状态。A 线程 lock()时,会调用 tryAcquire()独占该锁并将 state+1。此后,其他线程再 tryAcquire()时就会失 败,直到 A 线程 unlock()到 state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放 锁之前,A 线程自己是可以重复获取此锁的(state 会累加),这就是可重入的概念。但要注意, 获取多少次就要释放多么次,这样才能保证 state 是能回到零态的。
19.以 CountDownLatch 以例,任务分为 N 个子线程去执行,state 也初始化为 N(注意 N 要与线程个数一致)。这 N 个子线程是并行执行的,每个子线程执行完后 countDown()一次,state会 CAS 减 1。等到所有子线程都执行完后(即 state=0),会 unpark()主调用线程,然后主调用线程,就会从 await()函数返回,继续后余动作
使用 Class 类中的 forName()静态方法(最安全/性能最好)
20.注解: @Target 修饰的对象范围 @Retention 定义 被保留的时间长短 @Documented 描述-javadoc @Inherited 阐述了某个被标注的类型是被继承的 如果这个注解用与一个class,则也将用与这个class的子类
21.范型: 类型擦除:Java 中的泛型基本上都是在编译器这个层次来实现的。在生成的 Java 字节代码中是不包含泛型中的类型信息的,会被编译器在编译的时候去掉
22.红黑树:一颗自平衡的排序二叉树(左右两个子树的高度差的绝对值不超过1) 红黑树最重要的5点规则: 所以红黑树它是复杂而高效的,其检索效率O(lg n)。 Java的TreeMap就是通过红黑树实现的。 1、每个结点都只能是红色或者黑色中的一种。 2、根结点是黑色的。 3、每个叶结点(NIL节点,空节点)是黑色的。 4、如果一个结点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。 5、从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。
23.序列化对象以字节数组-》静态成员不保存 在类中增加 writeObject 和 readObject 方法可以实现自定义序列化策略 Transient 关键字阻止该变量被序列化到文件中在被反序列,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。 序列化(深 clone 一中实现):实现 Serializable 接口,然后把对象写到流中,再从流中读出来
24.ApplicationContext 由 BeanFactory 派生而来,提供了更多面向实际应用的功能。 ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础 上,还通过多个其他的接口扩展了 BeanFactory 的功能
25.webApplicationContext 是专门为web应用准备的,从 WebApplicationContext 中可以获得 ServletContext 的引用,整个 Web 应用上下文对象将作为属性放置到 ServletContext 中,以便 Web 应用环境可以访问 Spring 应用上下文。
26.Bean 定义了 5 中作用域,分别为 singleton(单例)、prototype(原型)、 request、session 和 global session 对有状态的 bean 使用 prototype 作用域,而对无状态的 bean 使用 singleton 作用域。
27.Spring bean 生命周期 实例化new(实例化) Ioc注入 (设置属性) setBeanName setBeanFactory setApplicationContext init-method 初始化 postProcessAfterInitialization(继承了beanPostProcessor) destory destory-method
28.spring4种注入: 构造器注入 set注入 静态工厂注入 (factory-method=“”) 实例工厂(factory-bean + factory-method) 自动装配 byType(autowired,+@Qualifilor byname) byName(@Resource,默认byName)
29.Jdk代理: 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理。 cglib代理: 利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 protocolbuffer 序列化
30.三次握手: 第一次握手:主机 A 发送位码为 syn=1,随机产生 seq number=1234567 的数据包到服务器,主机 B 由 SYN=1 知道,A 要求建立联机; 第二次握手:主机 B 收到请求后要确认联机信息,向 A 发送 ack number=(主机 A 的 seq+1),syn=1,ack=1,随机产生 seq=7654321 的包 第三次握手:主机 A 收到后检查 ack number 是否正确,即第一次发送的 seq number+1,以及位码 ack 是否为 1,若正确,主机 A 会再发送 ack number=(主机 B 的 seq+1),ack=1,主机 B 收到后确认 三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类 似路由器的转发动作
31.Zookeeper: 主从 半数成功 Zap协议:恢复模式和广播模式 四个阶段:选举阶段 - 发现阶段 - 同步阶段 - 广播阶段 超过半数的ack 首先选举自己。通过zxid来确定下一次投票的server 然后通过follower的zxid来确定同步点 Zookeeper核心是原子广播,zab协议
32.对象创建: 类加载(常量池是否有这个类的符号引用,是否被加载) 分配内存(指针碰撞和空闲列表,并发问题:cas+失败重试,tlab) 初始化零值 设置对象头(对象hash,gc分代年龄) 执行init方法
33.我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式有虚拟机实现而定,目前主流的访问方式有①使用句柄和②直接指针两种
34.直接使用双引号声明出来的 String 对象会直接存储在常量池中(String s = “aa”)。 Byte,Short,Integer,Long,Character,Boolean;这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据 integer会自动拆箱
35.数据库引擎: innoDB(b+树索引):B 树的每个节点对应 innodb 的一个 page,page 大小是固定的, 一般设为 16k。其中非叶子节点只有键值,叶子节点包含完成数据。 越多的索引,会使更新表变得很浪费时间
36.第一范式的目标是确保每列的原子性:如果每列都是不可再分的最小数据单元 第二范式要求每个表只描述一 件事情。 第三范式定义是,满足第二范式,并且表中的列不存在对非主键列的传递依赖 行级锁是一种排他锁,防止其他事务修改此行
37.一致性hash算法通过一个叫作一致性hash环的数据结构,实现这个环的起点是0,终点是2^32 - 1,并且起点与终点连接,环的中间的整数按逆时针分布,故这个环的整数分布范围是[0, 2^32-1] 对机器取模,放到这个环中 对存的对象取模,放到环中 从对象在环中的位置开始顺时针找机器存 对每个机器添加几个虚拟节点,解决负载问题:如何在动态的网络拓扑中分布存储和路由