四种引用类型
- 强引用
默认引用形式,比如
Object obj = new Object(); //只要obj还指向Object对象,Object对象就不会被回收
obj = null; //手动置null
回收机制:只有被赋予null时才会被回收,内存不足时抛出OutOfMemoryError
- 软引用
public class TestOOM {
private static List<Object> list = new ArrayList<>();
public static void main(String[] args) {
testSoftReference();
}
private static void testSoftReference() {
for (int i = 0; i < 10; i++) {
byte[] buff = new byte[1024 * 1024];
SoftReference<byte[]> sr = new SoftReference<>(buff);
list.add(sr);
}
System.gc(); //主动通知垃圾回收
for(int i=0; i < list.size(); i++){
Object obj = ((SoftReference) list.get(i)).get();
System.out.println(obj);
}
}
}
回收机制:内存足够时,不回收软引用类型;类型不足时,程序报错
- 弱引用
private static void testWeakReference() {
for (int i = 0; i < 10; i++) {
byte[] buff = new byte[1024 * 1024];
WeakReference<byte[]> sr = new WeakReference<>(buff);
list.add(sr);
}
System.gc(); //主动通知垃圾回收
for(int i=0; i < list.size(); i++){
Object obj = ((WeakReference) list.get(i)).get();
System.out.println(obj);
}
}
回收机制: 只要系统开始内存回收就被回收掉
- 虚引用
回收机制: 随时被回收
线程状态
状态 | 说明 |
---|---|
New | 初始状态,线程被构建,但是没有启动start()方法 |
RUNNABLE | 运行状态,就绪和运行两种状态被称为"运行中" |
BLOCKED | 阻塞状态 |
WAITING | 等待状态,等待其他线程通知 |
TIME_WAITING | 超时等待,等一段时间后返回 |
TERMINATED | 终止状态,表示当前线程执行完毕 |
使用数组实现栈
public class MyStack {
private int[] storage;//存放栈中元素的数组
private int capacity;//栈的容量
private int count;//栈中元素数量
private static final int GROW_FACTOR = 2;
//TODO:不带初始容量的构造方法。默认容量为8
public MyStack() {
this.capacity = 8;
this.storage=new int[8];
this.count = 0;
}
//TODO:带初始容量的构造方法
public MyStack(int initialCapacity) {
if (initialCapacity < 1)
throw new IllegalArgumentException("Capacity too small.");
this.capacity = initialCapacity;
this.storage = new int[initialCapacity];
this.count = 0;
}
//TODO:入栈
public void push(int value) {
if (count == capacity) {
ensureCapacity();
}
storage[count++] = value;
}
//TODO:确保容量大小
private void ensureCapacity() {
int newCapacity = capacity * GROW_FACTOR;
storage = Arrays.copyOf(storage, newCapacity);
capacity = newCapacity;
}
//TODO:返回栈顶元素并出栈
private int pop() {
count--;
if (count == -1)
throw new IllegalArgumentException("Stack is empty.");
return storage[count];
}
//TODO:返回栈顶元素不出栈
private int peek() {
if (count == 0){
throw new IllegalArgumentException("Stack is empty.");
}else {
return storage[count-1];
}
}
//TODO:判断栈是否为空
private boolean isEmpty() {
return count == 0;
}
//TODO:返回栈中元素的个数
private int size() {
return count;
}
}
数组和链表区别
数组 | 链表 |
---|---|
内存连续 | 内存不连续,通过指针联系在一起 |
查找容易,增加删除会移动大量元素 | 增加删除只需要修改对应的内存即可,查找需要从首元素开始顺着指针找 |
快速访问,很少插入和删除 | 经常删除插入元素 |
HashMap 和 Hashtable 的区别
HashMap | Hashtable |
---|---|
线程非安全 | 线程安全 |
效率高 | 效率低 |
null可以作为键,但是只可以有一个,可以有一个或多个键对应值为null | 键值不能为null |
默认初始大小11,扩充容量变为2n+1 | 默认初始大小为16,每次扩充容量变为2倍 |
链表长度大于阀值(默认为8),链表转化为红黑树 | 无 |
HashMap 和 HashSet 区别
HashMap | Hashtable |
---|---|
实现Map接口 | 实现Set接口 |
存储键值对 | 存储对象 |
使用key计算HashCode | 使用成员对象计算hashcode值,使用equals()方法判断对象相等性 |
== 和 equals 区别
HashMap | Hashtable |
---|---|
判断两个变量或实例是否指向同一个内存空间 | 判断内存空间值是否相等 |
内存地址进行比较 | 字符串内容比较 |
引用是否相同 | 值是否相等 |
HashMap 原理实现
- 拉链法,数组和链表相结合,创建一个链表数组,数组中每一个格都为链表,若遇到哈希冲突,则将冲突的值加到链表中即可。当链表长度大于默认阀值(8),将链表转为红黑树
- 长度为2的幂次方:
- 为了减少哈希碰撞,尽量将数据分配均匀;因为Hash值范围为-2147483648到2147483647,大概40亿的映射空间,内存放不下,用之前先对数组的长度取模运算
- 对应的算法为
hash&n-1
,取模算法只有当分母为 2 n 2^n 2n 才想等同于取模,采用位于算法能够提高效率
二叉查找树(Binary Search Tree)
特性:
- 左子树上所有节点值小于或者等于根节点值
- 右子树上所有节点值大于或者等于根节点值
- 左右子树也分别为二叉排序树
- 查找的最大次数等同于树的高度
缺陷:
多次插入会导致树不平衡,查找变为线性
红黑树(Red Black Tree)
特性:
- 节点是红色或者黑色
- 根节点是黑色
- 每个叶子节点都是黑色的空节点(Nil节点)
- 每个红色节点的两个子节点都是黑色
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
结构会保证从根节点到叶子节点长度不会超过最短路径2倍
ConcurrentHashMap
上下文切换
多线程编程中一般变成数量大于CPU核心个数,而一个CPU核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU采用的策略是为每个线程分配时间片并轮换,当一个线程用完并重新处于就绪状态让其他线程使用,这个过程就属于一个上下文切换
死锁的四个条件
- 互斥条件: 资源再某一个时刻只能被一个线程占用
- 请求与保持条件: 一个线程因请求资源而堵塞时,对已获得的资源保持不放
- 不剥夺条件: 线程已获得的资源在未使用完之前不得被其他线程夺取,只有自己使用完毕才能释放资源
- 循环等待条件: 多个线程形成收尾相接的循环等待资源
sleep和wait区别
sleep | wait |
---|---|
不释放锁 | 释放锁 |
暂停线程 | 暂停线程 |
暂停执行 | 线程间交互/通信 |
方法执行完,线程自动苏醒 | 线程不会自动苏醒,需要别的线程notify()或者notifyAll()方法;或者使用超时方法自动苏醒 |
线程中调用start()方法时会执行run()方法,为什么不直接调用run()方法?
调用start()方法会使线程进入就绪状态,当分配到时间片后就会开始运行;直接执行run()方法会把此方法当成一个普通方法在main()线程中执行,不是真正的多线程