文章目录
- 1、throw和throws有什么区别?
- 2、Colliection和Collections有什么区别?
- 3、Override(重写)和Overload(重载)有什么区别?
- 4、Math.round(5.5)等于多少?Math.round(-5.5)等于多少?
- 5、>>和>>>有什么区别?
- 6、java中比较器Comparator和Comparable有什么区别?
- 7、使用至少三种使得Map集合为线程安全的集合
- 8、HashSet的底层实现原理
- 9、HashSet集合如何检查重复?
- 10、如何在两个线程之间共享数据?
- 11、悲观锁和乐观锁在实现上有什么区别?
- 12、HashMap的长度为什么是2的冥次方?
- 13、阐述ConcurrentHashMap和Hashtable的区别?
- 14、引起线程上下文切换的原因有哪些?
- 15、简述造成死锁的原因有哪些?
1、throw和throws有什么区别?
throws子句:声明可能会出现的异常
throw语句:抛出异常
1、throw
关键字用来在程序中明确的抛出异常,相反,如果一个方法可能会出现异常,但没有能力处理这种异常,则可以用throws
子句来声明抛出异常;
2、throw用于方法内部,throws用于方法声明上
3、throw后跟异常对象,throws后跟异常类型
4、throw后只能跟一个异常对象,throws后可以一次声明多种异常类型
2、Colliection和Collections有什么区别?
1、Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
2、Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架
参考链接:https://blog.csdn.net/m0_46420991/article/details/119136931
3、Override(重写)和Overload(重载)有什么区别?
相同点:
- 都要求方法名相同。
- 都可以用于抽象方法和非抽象方法之间。
不同点:
- 重写要求参数签名必须一致,而方法重载要求参数签名必须不一致。
- 重写要求返回类型必须一致,而重载对此不做限制
- 重写只能用于子类覆盖父类的方法,重载用于同一个类的所有方法(包括从父类中继承而来的方法)
- 重写对方法的访问权限和抛出的异常有特殊的要求,而重载在这方面没有限制
- 父类的一个方法只能被子类重写一次,而一个方法在所在的类中可以被重载多次
4、Math.round(5.5)等于多少?Math.round(-5.5)等于多少?
Math.round是四舍五入,实际上就是:加0.5向下取整
所以Math.round(5.5)=6; Math.round(-5.5)=-5
5、>>和>>>有什么区别?
1、>>表示是带符号的右移:
按照二进制把数字右移指定数位,高位如符号位为正补零,符号位负补一,低位直接移除
2、>>>表示无符号的右移:
按照二进制把数字右移指定数位,高位直接补零,低位移除。
6、java中比较器Comparator和Comparable有什么区别?
Comparable接口用来定义对象的自然顺序,而Comparator接口通常用于定义用户自定义的顺序。Comparable接口总是只有一个,是由java.lang提供的定义好的接口,即不可修改。而Comparator是由java.util提供的,集合外部实现的排序,可以通过实现他的方法进行修改,可以有多个Comparator来定制对象的顺序。
总结
如果不需要自定义排序的话,简单排序实现Comparable即可。使Comparator可以实现不同的排序,而Comparable不可以实现。
参考链接:https://blog.csdn.net/qq_42461639/article/details/81503454
7、使用至少三种使得Map集合为线程安全的集合
第一种:使用hashtable
第二种:使用synchronizedMap
Map<String,Object> synchronizedMap= synchronizedMap(new Hashtable<String,Object>());
第三种:使用ConcurrentHashMap
Map<String,Object> concurrentHashMap=new ConcurrentHashMap<String,Object>();
8、HashSet的底层实现原理
HashSet存储元素的顺序并不是按照存入时的顺序,而是按照哈希值来存的,所以取数据也是按照哈希值取得。元素的哈希值是通过元素的hashcode方法来获取的,HashSet先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法,如果equals结果为true。HashSet就视为同一个元素。如果为false,就不是同一个元素
HashSet通过hashcode值来确定元素在内存中的位置,一个hashcode位置上可以存放多个元素,多个元素间通过重写的equals方法来判断是否是同一个
9、HashSet集合如何检查重复?
其实原理:HashSet会通过元素的hashcode()和equals()方法进行判断,当试图将元素加入到Set集合中,HashSet首先会使用对象的hashcode来判断对象加入的位置。同时也会与其他已经加入的对象的hashcode进行比较,如果没有相等的hashcode,HashSet就认为这个对象之前不存在,如果之前存在同样的hashcode值,就会进一步的比较equals()方法,如果equals()比较返回结果是true,那么认为该对象在集合中的对象是一模一样的,不会将其加入;如果比较返回的是false,那么HashSet认为新加入的对象没有重复,可以正确加入。
参考链接:https://blog.csdn.net/yt_19940616/article/details/90206441
10、如何在两个线程之间共享数据?
1.将数据抽象成一个类,并将数据的操作作为这个类的方法,这么设计可以和容易做到同步只要在方法上加"synchronized"
2.将Runnable对象作为一个类的内部类,共享数据作为这个类的成员变量,每个线程对共享数据的操作方法也封装在外部类,以便实现对数据的各个操作的同步和互斥,作为内部类的各个 Runnable 对象调用外部类的这些方法。
11、悲观锁和乐观锁在实现上有什么区别?
悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
参考链接:https://blog.csdn.net/qq_34337272/article/details/81072874
12、HashMap的长度为什么是2的冥次方?
HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同。想到的办法就是取模运算:hash%length,但是在计算机中取模运算效率与远不如位移运算(&)高。主要原因是位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。所以官方决定采用使用位运算(&)来实现取模运算(%),也就是源码中优化为:hash&(length-1)。
我们知道,当一个数是 2^n 时, 任意整数对2^n取模等效于:h % 2^n = h & (2^n -1) ,所以长度应设置为2^n(而且你会发现 2 ^ n - 1 的二进制都是1111…11这样的,很方便计算机计算)
13、阐述ConcurrentHashMap和Hashtable的区别?
ConcurrentHashMap和HashTable的区别主要体现在实现线程安全的方式上的不同:
底层数据结构:jdk7之前的ConcurrentHashMap底层采用的是分段的数组+链表实现,jdk8之后采用的是数组+链表/红黑树;HashTable采用的是数组+链表,数组是主体,链表是解决hash冲突存在的。
实现线程安全的方式:
- jdk8以前,ConcurrentHashMap采用分段锁,对整个数组进行了分段分割,每一把锁只锁容器里的一部分数据,多线程访问不同数据段里的数据,就不会存在锁竞争,提高了并发访问;jdk8以后,直接采用数组+链表/红黑树,并发控制使用CAS和synchronized操作,更加提高了速度。
- HashTable:所有的方法都加了锁来保证线程安全,但是效率非常的低下,当一个线程访问同步方法,另一个线程也访问的时候,就会陷入阻塞或者轮询的状态。
参考链接:https://blog.csdn.net/qq_38197844/article/details/109013959
14、引起线程上下文切换的原因有哪些?
上下文切换:
利用了时间片轮转的方式,CPU 给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务,任务的状态保存及再加载,这段过程就叫做上下文切换
- 当前执行任务的时间片用完之后,系统 CPU 正常调度下一个任务:
- 当前执行任务碰到 IO阻塞,调度器将此任务挂起,继续下一任务;
- 多个任务抢占锁资源,当前任务没有抢到锁资源,被调度器挂起,继续下一任务;
- 用户代码挂起当前任务,让出CPU时间:
- 硬件中断
15、简述造成死锁的原因有哪些?
死锁:
就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。
死锁的四个必要条件:
①互斥条件
某资源只能被多个进程互斥的访问。
②请求和保持条件
进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放
③不可抢占条件
进程已获得的资源在未使用之前不能被抢占,只能在进程使用完由自己释放
④循环等待条件。
发生死锁,必然存在一个资源-资源的循环请求链,都被阻塞
往期回顾:java面试题每日一练(3)