【Java 知识点】 别人家的孩子的面经贴(上)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了【Java 知识点】 别人家的孩子的面经贴(上),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含10756字,纯文字阅读大概需要16分钟。
内容图文
写在前面
牛客网上看到的人家的实习面试题目,学习学习
1. Java中的队列有哪些?哪些是线程安全的?
1.1 Java中的队列分为两类:
1.1.1 阻塞队列,实现了阻塞接口BlockingQueue(BlockingQueue接口规定队列:当生产线程向队列添加元素但队列已满时,生产线程被阻塞直到队列有剩余空间;当消费线程从队列移除元素但队列为空时,消费线程被阻塞直到队列有元素), 位于 java.util.concurrent 并发包中,包括:
ArrayBlockingQueue<>(capacity) :基于数组实现的有界队列;
DelayQueue :基于优先级堆实现,支持时延的无界调度队列;
DelayedWorkQueue in ScheduledThreadPoolExecutor : 基于优先级堆实现,支持时延实现周期性的无界调度队列;
LinkedBlockingDueue<>(省略/capacity) :基于链表实现的可选无界双端队列;
LinkedBlockingQueue<>(省略/capacity) :基于链表实现的可选无界队列;
LinkedTransferQueue:基于链表实现可选无界队列(采用预占模式,当队列为空时,提交一个null进队列,消费线程发现null进行等待,省略了链表的判空步骤);
PriorityBlockingQueue :基于优先级堆实现的无界队列;
SynchronousQueue<>() :没有缓冲的阻塞队列(1进1出);
1.1.2 非阻塞队列,没有实现BlockingQueue接口的:
ArrayDeque: 基于数组实现的双端队列(位于 java.util包中);
PriorityQueue:基于优先级堆实现的,维护一个有序列表的无界队列(位于 java.util包中);
ConcurrentLinkedDeque:基于链表实现的,线程安全的双端队列(位于 java.util.concurrent包中);
ConcurrentLinkedQueue:基于链表实现的,线程安全的队列(位于 java.util.concurrent包中);
1.2 Java线程安全的队列
首先要知道线程安全和非线程安全的概念:
线程安全就是同一个对象对多线程访问时,采用加锁机制,对该对象的某个关键数据进行保护,其他线程不能进行访问直到该线程访问操作完,其他线程才可进入访问数据,不会出现不同的线程在同一时刻读取该对象的数据不一致或者数据污染的问题;
非线程安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据;
1.2.1 Java队列中线程安全的有:
ArrayBlockingQueue<>(capacity,boolean) :并发控制使用经典的两条件法(使用1个ReentrantLock结合2个Condition)
DelayQueue:并发控制通过一个final ReebtrantLock非公平锁;
DelayedWorkQueue in ScheduledThreadPoolExecutor :
LinkedBlockingDeque:并发控制使用经典的两条件法(使用2个ReentrantLock结合2个Condition)
LinkedBlockingQueue:并发控制使用经典的两条件法(使用1个ReentrantLock结合2个Condition)
LinkedTransferQueue:是ConcurrentLinkedQueue,SynchronousQueue ,LinkedBlockingQueue的超类;
PriorityBlockingQueue :并发控制使用经典的条件法(使用1个ReentrantLock结合1个Condition)
SynchronousQueue:内部没有实现锁,但是使用LockSupport控制线程,实现"wait-free"算法(即CAS算法, CompareAndSwap,比较的交换,保证原子操作)控制基础链表的head和tail;
(阻塞队列都是线程安全的,使用一个锁或者两个锁控制入队和出队, 它们都位于java.util.concurrent 并发包)
ConcurrentLinkedQueue: 采用非阻塞的方式实现线程安全队列,它采用CAS算法来实现;
ConcurrentLinkedDeque: 采用非阻塞的方式实现线程安全队列,它采用CAS算法来实现;
(非阻塞队列实现线程安全,基于循环CAS实现, 它们也都位于java.util.concurrent 并发包)
1.2.2 Java队列中非线程安全的有:
ArrayDeque: 非线程安全队列,无同步策略,不支持多线程安全访问(位于 java.util包中);
PriorityQueue:非线程安全队列,无同步策略,不支持多线程安全访问(位于 java.util包中);
并发编程中如果要用ArrayDeque和PriorityQueue可以替换成线程安全的ArrayBlockingQueue和PriorityBlockingQueue
2. Java的集合?哪些是线程安全的?介绍hashmap和connurrentHashMap和其他线程安全的集合。hashMap的扩容?ConcurrentHashMap什么时候不线程安全?
2.1 Java中的集合主要继承Collection和Map两个根接口,Collection又派生了List,Queue,Set三个子接口,因此Java集合主要分为四类:List,Queue,Set,Map:
List | Queue | Set | |||
---|---|---|---|---|---|
线程安全 | 非线程安全 | 线程安全 | 非线程安全 | 线程安全 | 非线程安全 |
CopyOnWriteArrayList | ArrayList | ArrayBlockingQueue | ArrayDeque | CopyOnWriteArraySet | HashSet |
CollesynchronizedList | LinkedList | PriorityBlockingQueue | PriorityQueue | TreeSet | |
Vector | DelayQueue | ||||
Stack | LinkedBlockingDeque | Map | |||
LinkedBlockingQueue | 线程安全 | 非线程安全 | |||
LinkedTransferQueue | Hashtable | hashmap | |||
SynchronousQueue | Collections.synchronizedMap | TreeMap | |||
DelayedWorkQueue | ConcurrentHashMap |
2.2 介绍hashmap和connurrentHashMap和其他线程安全的集合
HashMap描述一一映射的关系,是非线程安全的数据结构,如果想在多线程下安全的操作 map,主要有以下解决方法:
(1)使用HashTable线程安全类,HashTable实现线程安全,是将所有访问和操作数据的方法被synchronized修饰(加同步锁),对整张哈希表加锁;
(2)使用Collections.synchronizedMap方法,对方法进行加同步锁(本质和HashTable类似);
(3)ConcurrentHashMap把整个整张哈希表分为默认N个Segment,每个片段提供相同的线程安全(ConcurrentHashMap不是通过加锁,而是通过数据被volatile修饰,保证数据的原子性),效率提升N倍(默认N=16)
为什么connurrentHashMap的效率高于hashtable? Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,只要有一个线程在修改操作hashtable的对象,其他线程只能阻塞等待锁被释放,并发编程中性能差,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术(每个Segment由自己的ReentrantLock保护);
2.3 HashMap,HashTable,ConcurrentHashMap如何扩容
(1) HashMap底层由数组+链表实现,可以存储null键和null值, 参数initialCapacity(默认16),loadFactor(默认75%),扩容策略:newsize = oldsize*2;
(2)HashTable底层由数组+链表实现,不可以存储null键和null值,参数initialCapacity(默认11),loadFactor(默认75%),扩容策略:newsize = oldsize*2+1;
(3)ConcurrentHashMap底层由分段的数组+链表实现,参数initialCapacity(默认16),loadFactor(默认75%),concurrencyLevel(维护Segment数组大小,默认16,或者自定义参数最接近2的n次方的数),扩容策略:newsize = oldsize*2;
2.4 ConcurrentHashMap什么时候不线程安全
虽然ConcurrentHashMap是线程安全的类,但是抵不住外部程序造成的不线程安全的情况
案例
ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put("key", 0);
ExecutorService executorService = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
/** 这块代码造成concurrentHashMap线程不安全:
*
* int value = concurrentHashMap.get("key");
* 首先,concurrentHashMap的get方法是线程安全的,一个线程在获取的时候,其他线程阻塞,
* 而且concurrentHashMap通过HashEntry存储的value是volatile的,保证其原子性
*
* concurrentHashMap.put("key", ++value);
* concurrentHashMap的get方法是线程安全的,一个线程在修改数据的时候,其他线程阻塞,
*
* 那问题出在哪呢
*/
int value = concurrentHashMap.get("key");
concurrentHashMap.put("key", ++value);
}
});
}
executorService.shutdown();
Thread.sleep(3000);
System.out.println(concurrentHashMap.get("key"));
//运行三次打印结果:
"C:\Program Files\Java\jdk-11.0.3\bin\java.exe" "-javaagent:E:\IntelliJIDEA\IntelliJ IDEA Community Edition 2019.3.1\lib\idea_rt.jar=64471:E:\IntelliJIDEA\IntelliJ IDEA Community Edition 2019.3.1\bin" -Dfile.encoding=UTF-8 -classpath E:\Projects\project_java\LeetCodeAns\out\production\LeetCodeAns javaKown.JAVA数据结构
998
995
999
Process finished with exit code 0
其实很明显,是因为单个线程中获取和修改数据的不同步造成的,比如说
线程 i 在获得get的锁,获取到“key”的value为325后释放锁->
线程 j 获得get的锁, 假设线程 i 还未获取到set的锁,还没有修改“key”的value,这时线程 j 获取到“key”的value也是325->
线程 i 还获取到set的锁,修改“key”的value为326->
线程 j 还获取到set的锁,修改“key”的value为326;.......
但是根据线程安全的需求,在线程 i , j 完成任务后“key”的value应该是327才对。
解决方法
1. 外部程序中单个线程的关键代码块加同步锁,但是这样做的话,跟多线程访问修改HashTable差不多,单个线程锁着整个哈希表,效率降低,在实际开发过程中要仔细思考灵活应用
ExecutorService executorService = Executors.newFixedThreadPool(1000);
Object lock = new Object();
for (int i = 0; i < 1000; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
// 关键代码区加同步锁
synchronized (lock) {
int value = concurrentHashMap.get("key");
concurrentHashMap.put("key", ++value);
}
}
});
}
executorService.shutdown();
//运行三次打印结果
"C:\Program Files\Java\jdk-11.0.3\bin\java.exe" "-javaagent:E:\IntelliJIDEA\IntelliJ IDEA Community Edition 2019.3.1\lib\idea_rt.jar=65355:E:\IntelliJIDEA\IntelliJ IDEA Community Edition 2019.3.1\bin" -Dfile.encoding=UTF-8 -classpath E:\Projects\project_java\LeetCodeAns\out\production\LeetCodeAns javaKown.JAVA数据结构
1000
1000
1000
Process finished with exit code 0
2. 使用原子类(没有用过原子类,不敢写,请读者自行查阅)
3. 线程有几个状态?实现多线程的方法?线程池有几个状态?具体介绍一下线程池的每一个参数是什么意思?线程具体怎么分配?哪些线程会被销毁?Java多线程在操作系统底层是怎么实现的?Java多线程在不同内核数的CPU下有什么区别?
3.1 线程有5个状态
new:初始化一个线程;
Runnable:线程处于可运行状态;
Running: 线程处于运行状态;
Blocked: 线程进入阻塞状态;
Dead: 线程死亡,被销毁;
3.2 实现多线程有四种方法
(1)继承Thread类;(2)实现Runnable接口;(3)实现Callable接口;(4)使用线程池。
3.3 线程池有5个状态
Running: 线程池接受新任务,并处理阻塞队列内的任务;
Shutdown: 线程池不接受新任务,但会继续处理线程的任务和阻塞队列内的任务;
Stop:线程池不接受新任务,且中断正在执行的线程和抛弃阻塞队列的任务;
Tidying:线程池的ctl计数器清零;
Terminated:线程池彻底终止;
3.4 具体介绍一下线程池的每一个参数是什么意思?线程具体怎么分配?哪些线程会被销毁?
3.4.1 ThreadPoolExecutor的参数
int corePoolSize : 核心线程数, 当任务提交进来时,当核心线程未被任务占满时,任务先进入核心线程
int maximumPoolSize : 线程池可容纳最多线程数
long keepAliveTime, TimeUnit unit : 当前线程数大于核心线程数时,非核心线程的等待被执行的等待时间
BlockingQueue<Runnable> workQueue : 阻塞队列,当核心线程被占满,新的任务会进入阻塞队列等待执行
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler) : 拒绝策略,阻塞队列已满或无法将新任务添加到线程池时应对方案
3.4.2 线程池的线程怎么分配
线程池提交的任务先抢占核心线程,当核心线程满后,进入阻塞队列排列,当阻塞队列满后,抢占非核心线程
3.4.3 哪些线程会被销毁
非核心线程在KeepAlive时间内没有Running会被销毁
3.5. Java多线程在操作系统底层是怎么实现的?
(当线程在用户空间内实现时(ULT),OS无法感知线程的存在,只能看到用户进程,ULT线程由用户进程自己管理)
Java线程属于KLT内核线程,内核通过调度器(Schedular)对线程进行调度,将线程的任务分配到指定的处理器。
3.6. Java多线程在不同内核数的CPU下有什么区别?
当多线程数多于CPU数时,OS使用时间片机制,通过调度器(Schedular)对线程进行调度,频繁进行线程求换。
一般说来,线程池线程数设 CPU核心数/(1-阻塞系数)
如果是计算密集型应用(CPU利用率高,空闲时间少,阻塞系数低),则线程池大小设置为CPU个数+1
如果是IO密集型应用(CPU利用率低,空闲时间多,阻塞系数高),则线程池大小设置为2*CPU个数+1
写在后面:
下一节问题
4. Java内存模型讲一下,方法区必须存在吗?
5. Java的gc,包括young GC和Old GC,G1回收器跟其他回收器相比最大的区别在哪儿?
6. 数据库Innodb的索引是什么数据结构?怎么建立索引?索引和数据的关系?选主键的时候注意什么?如果选字符串形式的IP地址作为主键,会有什么问题?
7. 类的加载机制?Java字节码可以被改吗?怎么改?
8. NIO介绍一下,在Java语言中怎么实现的?
9. Socket编程,TCP/IP?
内容总结
以上是互联网集市为您收集整理的【Java 知识点】 别人家的孩子的面经贴(上)全部内容,希望文章能够帮你解决【Java 知识点】 别人家的孩子的面经贴(上)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。