文章目录
- Java
-
- HashMap
- HashCode
- equals
- equals 和 ==
- sleep()和wait()的区别
- 同步锁
- synchronized使用
- Lock的使用
- synchronized和Lock的区别
- synchronized和volatile
- GC垃圾回收机制
- Java对象声明周期
- 单例模式
- Java线程
- Object方法
- 深克隆和浅克隆
- Java继承
- 面向对象的特征
- public,private,protecter,default
- Java精度转换
- int和Integer有什么区别
- &和&&的区别
- char
- 抽象类
- Java内存泄漏
- 静态变量和实例的区别
- final
- try里面加return,finally里面代码会不会执行
- final、finally、finalize的区别
- Error和Exception有什么区别
- String
- Java 容器
- Java 引用
- 内存泄漏、内存溢出
- JVM
- Java内部类
- Java锁
- 序列化和反序列化
Java
HashMap
hash碰撞解决方法
(1) 开放定址法
Hash碰撞之后就向下寻找空的存储空间
(2) 链地址法(拉链法)
数组和链表结合,碰撞之后就插入链表(静态数组和动态数组的结合)
(3) 再hash法
碰撞之后就再生成一hash表
HashMap扩容
(1)为什么要扩容,随着哈希表插入的数据越来越多,查找效率越来越低(哈希碰撞越来越多)
(2)直接扩大,重新生成新的最大值,重新生成hashcode
(3)Java大于数组长度的0.75,开始扩容,原来数组的两倍大
HashMap链表查找效率低怎么解决:
JDK1.8以后链表长度大于8时,自动转换成红黑树
HashCode
hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值,方便字符串和类型进行比较
两个对象equal返回true,那么两个对象的hashCode()值也相同,反之不亦然!
HashCode 就是Hash值,Java中一般选用 2 N 2^N 2N来做模,好处就是取模速度快,原理如下:
static int Fun(int x,int length){
return x & (length - 1); //对(2^N-1)来说,取模直接&即可
}
为什么需要HashCode——方便!
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,初学者可以简单理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
equals
equals在Object对象中直接比较两个类的引用地址,而在一些类型如String 、Math、Integer、Double中,对equals进行了重写,调用equals的时候是对内容进行比较,而不是简单的对引用地址进行比较。
- 自反性
x.equals(x) == true;
- 对称性
x.equals(y) == y.equals(x);
- 传递性
if(x.equals(y) && y.equals(z)){
x.equals(z) == true;
}
- 一致性
x.equals(y) == x.equals(y);
- 与null比较
x.equals(null) == false;//x不为null
equals 和 ==
- 对于基本类型,用==来判断两个值是否相等
- 对引用类型(对象),用==来判断是否是同一个引用,而用equals来判断二者内容是否相等(例如String,此时的equals被重写了)
sleep()和wait()的区别
(1)本质区别是sleep()不会释放同步锁,wait()释放同步锁
(2)sleep是Thread的方法,wait()是Object的方法
(3)wait(),notify 和 notifyAll 必须要在同步控制的方法中使用,而sleep()可以子任何地方使用
(4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
(5)由于sleep不会释放同步锁,容易导致死锁问题,因此更推荐用wait
同步锁
产生原因
- 线程之间存在共享数据。
- 线程在具体执行时,会先拷贝主存数据到线程本地(CPU缓存),操作完成后再把结果从线程本地刷到主存。多线程情况下就会产生并发问题。
意义
当多个线程共用一块资源的时候,会出现资源抢占,加了同步锁之后,使得同一段时间只有一个线程会获得资源,其他线程处于等待状态。
主要实现方式有
synchronized,Lock
synchronized使用
synchronized是Java关键字,在JVM层面实现线程锁
当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。
对于一个类,在其synchronized方法A内是永远无法调用他的synchronized方法B的,因为同时只能有一个锁,B方法要等A方法释放锁才能调用。
Lock的使用
Lock是一个接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
lock()
如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try-catch块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
Lock lock = new ReentrantLock();
lock.lock();
try {
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
}finally {
lock.unlock();
}
tryLock()
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
Lock lock = ...;
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}
tryLock(long time, TimeUnit unit)
方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。使用方法同上。
lockInterruptibly
lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}
当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。因此当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。
synchronized和Lock的区别
- synchronized是Java关键字,是在JVM层面控制同步的。Lock是一个接口,在代码JDK层面进行控制的
- synchro