知识总结篇

知识总结篇,第1张

知识总结篇

文章目录

java基础

equals和hashCode方法String特性StringBuilder和StringBuffer java容器

集合分类SetListQueueMapHashMap原理容器中的设计模式 java并发

创建线程线程方法jdk线程池支持-Executor框架ThreadLocal容器volatile关键字互斥同步锁AQS组件线程安全级别锁优化

自旋锁锁消除锁粗化轻量级锁偏向锁 JVM

内存模型堆内存结构如何判断对象是否死亡垃圾收集算法常见垃圾收集器类加载机制类加载器双亲委派模型打破双亲委派模型 Mysql

索引相关数据结构和算法B+树索引

使用B+树做索引的优势聚集索引辅助索引 索引优化

表数据量最左前缀原理选择性前缀索引主键选择范围查询 事务

四大特性ACID并发事务可能会造成的问题事务的隔离级别 Mysql主从复制原理 Spring

@Transation事务传播

java基础 equals和hashCode方法

重写equals()方法后应当也重写hashCode()方法,保证被视为等价的对象哈希值也相同。

String特性

String a = “a”; 先检查字符串常量值中有没有"a"字符,若无则新建一个,若有直接将地址指向变量a。

String a = new String(“a”); 直接在堆内存中创建一个新String对象。

StringBuilder和StringBuffer

StringBuilder线程不安全,但性能高。

java容器 集合分类

Collection和Map

Collection分为Set、List、Queue

Set

TreeSet:红黑树实现,有序,查找效率较慢

HashSet:哈希表实现,无需,查找效率高

linkedHashSet:哈希表和双向链表实现,有序,查找效率高

List

ArrayList:数组实现。扩容:初始化为0,第一次add时,默认扩容到10,若后面length>10,则扩容1.5倍左右,将原数组拷贝到新数组中。

多线程下使用Collections.synchronizedList();包装,得到线程安全的List。或使用JUC包下的CopyOnWriteArrayList。

CopyOnWriteArrayList:读写分离,写加锁到新数组,读老数组。读的数据可能不实时。

Vector:和ArrayList类似,线程安全

linkedList:双向链表实现,只能顺序访问,可快速插入删除,还可以用作堆栈、队列、双向队列

Queue

linkedList:可实现双向队列

PriorityQueue:基于堆结构实现,可实现优先队列

Map

TreeMap:红黑树实现

HashMap:哈希表实现

HashTable:哈希表实现,线程安全,已弃用

ConcurrentHashMap:线程安全,1.7分段锁,1.8 CAS *** 作失败则Synchronized。

linkedHashMap:双向链表和哈希表实现

HashMap原理

默认大小16,插入时,将key的hashCode值通过扰动函数得到hash值,然后通过hash%n取模(取余),优化为hash&(n-1),得到存放的下标位置,然后存放键值对。若哈希冲突,判断key是否相等(使用==和equals),若相等则覆盖,不相等则插入链表头部。1.8之后若链表大于8,转化为红黑树。

扩容:若put后size大于容量*0.75(默认负载因子),新建一个2倍容量数组,重新计算hash并存入新数组。

为什么线程不安全:

1、死循环:多线程同时扩容,链表头插法,3指向7,另一个线程7指向3,形成循环链表,在get时会死循环。

2、数据覆盖:多线程put,刚好hash冲突,覆盖其他线程put的值。

为什么hashMap大小需要是2的幂次方:

因为hash值范围太长,需要取模运算计算,当n是2的幂次方时,hash%n=hash&(n-1),二进制位运算与,比取余数效率高

为什么负载因子是0.75:

时间和空间的权衡,若负载因子太小,则浪费空间,但链表和红黑树高度比较低, 查询快。若太大,节省空间,但查询慢

容器中的设计模式

迭代器模式、适配器模式(asList(T… a))

java并发 创建线程

继承Thread类

实现runnable接口

实现Callable接口

实现runnable和callable接口只能当做一个多线程任务,需要通过Thread类来调用。

线程方法

在Synchronized语句中:

obj.notify() 随机选择一个线程唤醒

obj.wait() 当前线程在这个对象上等待

Thread方法:

Thread.join() 当前线程调用另外一个线程的join方法,表示暂停当前线程,等待另一线程结束再运行

Thread.yield() 当前线程让出cpu资源。

Thread.sleep(milliesec n) 当前线程睡眠n毫秒

Thread.setPriority(int n) 设置线程优先级,1-10,数字越大优先级越高

jdk线程池支持-Executor框架

池化设计优点:初始预设资源,防止重复获取资源连接的性能消耗。

Executors–线程池工厂

主要线程池:

Executors.newCachedThreadPool(int n) – 返回固定线程数量的线程池

Exectors.newFixedThreadPool() – 返回自适应线程数量的线程池

Executors.newSingleThreadPool() – 返回单线程数量的线程池

Executor的shutdown()方法,会等待线程都执行完毕后关闭。

ThreadLocal容器

线程私有的Map容器,主要用于数据隔离,存放线程私有的对象,如线程用户相关的上下文数据等,防止线程不安全。

但是使用完一定要记得remove,不然容易造成内存泄露问题。

spring中的事务就用到了Threadlocal来存放数据库链接,保证事务的隔离性。

volatile关键字

只修饰变量,保证变量的可见性和有序性,防止指令重新排序。

原理:编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的 *** 作与其他内存 *** 作一起重排序。不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取变量时总会返回最新写入的值。

对象实例化:

1.分配内存

2.初始化对象

3.将内存地址赋值给引用

互斥同步锁

Synchronized – jvm实现

通过监视器锁monitor对象实现,进入时monitor计数器+1,退出-1。

同步代码块,字节码中有monitorenter和monitorexit。同步方法中,常量池中有ACC_SYNCHRONIZED标识符。

底层通过调用 *** 作系统的Mutex Lock实现。

ReentrantLock – jdk实现JUC中的锁

区别:

jvm实现、jdk实现。

ReetrantLock等待可中断。

ReentrantLock可以是公平锁。创建时参数为true,默认为false,非公平锁。

AQS组件

CountDownLatch 维护一个计数器,每次调用countDown方法,计数器减1,直到减到0时,唤醒其他等待线程。

CyclicBarrier 循环屏障,调用await方法,计数器减1,并且当前线程进入等待,直到减到0,继续执行。

Semaphore 信号量,控制每次只有几个线程可以同时访问。

FutureTask 封装Callable,可获得任务执行结果,或者取消任务执行。

BlockingQueue 阻塞队列接口,实现:

FIFO队列–linkedBlockingQueue、ArrayBlockingQueue

优先级队列–priorityBlockingQueue

线程安全级别

不可变

不可变的对象,天生是线程安全的,eg,String、final修饰的基本数据类型、枚举类型

悲观锁(互斥锁)

Synchronized、ReentrantLock

乐观锁(可重入锁)

CAS(CompareAndSwap比较并交换)、AtomicInteger(方法调用了CAS)

ABA问题:

无锁(无同步)

不涉及共享数据的方法,不会有线程安全问题。eg,方法中的局部变量,线程私有的,存储在虚拟机栈中。

锁优化 自旋锁

JDK1.6引用了自适应的自旋锁,在线程获取被锁定的共享资源时,进行忙循环获取锁,自适应表示循环的次数由上一次在同一个锁上的自旋次数和锁的拥有者的状态决定。

锁消除

被检测出不存在锁竞争的共享数据的锁进行消除。

锁粗化

检测出频繁的加锁和解锁都是用于同一个对象,将会把锁的范围扩大。

轻量级锁

JDK1.6引入了偏向锁和轻量级锁,使锁分为了四个状态:无锁、偏向锁、轻量级锁、重量级锁。先采用CAS来更新锁状态,若失败,被其他线程抢占,则膨胀为重量级锁。

偏向锁

锁偏向于第一个获取到锁的线程,这个线程在之后获取该锁时,不用进行锁 *** 作。若有其他线程开始获取这个锁对象时,此时偏向锁恢复无锁状态或者轻量锁状态。

JVM 内存模型

线程共享区:堆、方法区

线程私有区:虚拟机栈、本地方法栈、程序计数器

堆:存放对象的内存区域、虚拟机垃圾处理的主要区域、所以也称为GC堆。

方法区:对象加载时的类信息、静态变量、常量值等。

虚拟机栈:线程在执行具体方法时,加载的方法参数、常量池等引用信息。

本地方法栈:记录本地方法在执行时的参数、常量池等引用信息。

堆内存结构

新生代(Eden、survivor0、survivor1)、老年代

新对象一般会在Eden区创建,一次垃圾回收之后,如果对象还存活,会被移到s0、s1中,对象年龄+1.

默认年龄大于15的对象,将被移入到老年代。年龄阈值可配置。大对象和长期存活的对象会直接进入老年代。

当Eden内存不足时,会进行minor GC,老年代内存不足会进行Full Gc,速度比MinorGc慢十倍以上。

如何判断对象是否死亡

引用计数法:每有一个引用,计数器+1,失效则-1。存在循环依赖的问题,所以没有被java虚拟机采用。

可达性分析法:当对象到Gc Roots根节点没有任何引用链,证明对象已不可用。

垃圾收集算法

标记-清除算法:效率不高,容易产生大量不连续空间碎片,导致无法给大对象分配内存。

复制算法:速度快,但占用空间。

标记-整理算法:不会产生内存碎片,但效率较低。

分代收集算法:新生代-复制算法。老年代-标记清除/整理

常见垃圾收集器

Serial串行收集器:单线程收集器,简单高效,运行时必须暂停其他线程。

ParNew收集器:Serial的多线程版本。可与CMS收集器配合使用。

Parallel Scavenge收集器:JDK1.8的默认收集器,多线程,重点关注吞吐率,即cpu使用率。

CMS收集器:Concurrent Mark Sweep收集器:基于标记清除算法,并发收集器。

运行过程:

初始标记、暂停其他线程,对ROOT节点对象进行快速标记,速度很快。

并发标记、并行进行标记,不影响运行线程,但会遗漏正在创建的对象。

重新标记、对遗漏的对象进行重新标记、比初始标记时间稍长。

并发清除、并发对垃圾对象进行清除。

G1收集器:针对多cpu、大内存服务器的收集器。

类加载机制

第一次使用时,对象创建是动态加载的,而不是一次性加载所有对象。

类生命周期:加载-验证-准备-解析-初始化-使用-卸载

解析可能在初始化之后,因为动态绑定时才能决定需要解析哪些方法和变量。

类加载器

启动类加载器Bootstrap ClassLoader:C++实现,加载lib包下的类

扩展类加载器Extension ClassLoader:java实现,加载lib/ext包下的类

应用程序加载器Application ClassLoader:java实现,加载用户目录下的类。程序中默认类加载器。

自定义类加载器User ClassLoader

双亲委派模型

加载时从父类加载器由上到下进行加载,父类无法加载则交给子类加载器加载。

打破双亲委派模型

1、自定义类加载器,重写loadClass()方法。

2、线程上下文类加载器

Mysql 索引相关数据结构和算法

顺序查找法:将数据有序排列,顺序查找。

二分查找法:将数据有序排列,将被元素与中点元素比较,然后将查找区间缩小一半,继续二分查找。

二叉查找树:左子树小于根,右子树大于跟。树分布越平衡,查找效率越高。

平衡二叉树:基于二叉查找树,任何节点的两个子树的高度最大差为1。维护代价较大。

B+树:有序数组链表+平衡多叉树。由B树和索引顺序访问方法演化而来。所有数据都是按Key值大小顺序存放在同一层的叶子节点上,由各叶子节点指针进行连接。

B+树索引 使用B+树做索引的优势

1、非叶子节点不存放数据,只存放索引,所以可以使树高更低,查询底层节点的磁盘I/O次数会更少。

2、叶子节点之间维护了一个有序的链表结构,所以范围查询的效率更高。

聚集索引

InnoDB引擎,数据文件本身就是索引文件,叶子节点data域中保存了完整的数据记录,索引key就是数据表的主键。

辅助索引

叶子节点data域存储相应记录主键的值,首先检索辅助索引获得主键的值,然后从主索引中获得记录数据。

索引优化 表数据量

若表数据量很小,没有必要建索引。2000条数据以下。

最左前缀原理

联合索引abc,若查询ac条件,只生效a列的索引。

选择性

并不是所有where条件中的列都需要添加索引,在访问表很少一部分数据时添加索引才有意义。

比如性别,可取值范围很小,称为低选择性,添加索引没有意义。

比如姓名,重复概率比较小,取值范围比较广,称为高选择性,添加索引效果很好。

前缀索引

若表字段较长,可建立固定前缀长度的索引,使索引key变短,减少索引文件大小和提高维护的性能。

主键选择

若无特殊业务要求,使用自增主键索引效率最高。

范围查询

索引最多可用于一个范围查询,且遵循最左前缀原则。

事务 四大特性ACID

原子性、一致性、隔离性、持久性

并发事务可能会造成的问题

脏数据、丢失修改、不可重复读(两次读数据的值不一致)、幻读(两次读数据有无不一致)

事务的隔离级别

从弱到强:可读未提交、可读已提交、可重复读、可串行化

默认为:可重复读

分布式事务中,必须设置隔离级别为可串行化。

Mysql主从复制原理

1、Master将数据更新记录到二进制日志中(binary log)中

2、Slave中有一个I/O线程不停监听Mster的binlog日志,若有更新,则将binlog复制到Slave服务器中存入relaylog中继日志。通常存于 *** 作系统os缓存中,开销较小。

3、Slave中有一个SQL线程从中继日志中读取事件,并重做relaylog日志,更新Slave数据库数据,使主从一致。

Spring @Transation

若加在方法上,只能作用在public方法上,因为spring的实现中会判断方法类型,为了解决类中自调用而事务无法生效的问题。

因为自调用无法调用代理类中的方法,而是直接调用本身类中的方法。

事务传播

默认级别为required,若无事务,新建事务,若有事务,加入同一个事务。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5703698.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存