前言
这篇文章主要讲解java开发中常见的面试题
主要来源是
尚硅谷Java大厂面试题第3季,跳槽必刷题目+必扫技术盲点(周阳主讲)
看之前可了解我之前做过的一些文章进行补充
java基础知识之面试题
通过学习该视频,以及翻阅源码以及自已的理解
主要博文如下
1. java基础
该知识涉及jvm的内存加载
以下代码可能会涉及这部分知识
代码如下
class test{
public static void main(String[] args) {
String str1 = new StringBuilder("58").append("tongcheng").toString();
System.out.println(str1);
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
System.out.println();
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
System.out.println(str2 == str2.intern());
}
}
通过代码输出
该值为
该解释以上两个链接可参考
最主要的原理还需要通过翻阅源码
- 因为java这个字符串在执行string-Builder.toString()之前就已经出现过了,字符串常量池中已经有了它的引用,不符合intern()方法首次遇到的原则。
- 有一个初始化的Java字符串(JDK出娘胎自带的),在加载sun.misc.Version这个类的时候进入常量池
主要的代码执行步骤为
system输出的时候会默认调用initializeSystemClass() 之后去Version
package sun.misc;
//反编译后的代码
public class Version {
private static final String launcher_name = "java";
...
}
sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化,而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue〉做默认初始化,此时被sun.misc.Version.launcher静态常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable里了。
1.1 力扣算法
此处只涉及力扣第一道算法
最主要是如何通过优化算法以及如何进行选择最优的算法
可看我之前的文章
【leetcode】数组-两数之和
涉及到的其他算法也可以参考我博客中的算法专栏
算法专栏
2. JUC
涉及这部分底层的知识可参考我之前的文章
JUC高并发编程从入门到精通(全)
关于这部分的线程进程的相关知识可查看我之前的文章
2.1 可重入锁
- 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的的内层方法会自动获取锁(前提是锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。
- Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁
必须是同一个对象,而且一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入,自己可以获取自己的内部锁
锁的机制有显式(lock,通过人为进行锁定或者解锁,也有ReentrantLock的可重入锁)、隐式(Synchronized)
可重入锁的验证:
(主要是验证了如果外层进入了,内层的锁也是可以直接进入,而且不用撤回锁)
同步代码块
public class ReentrantLockDemo2 {
Object object = new Object();
public void sychronizedMethod(){
new Thread(()->{
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"外层....");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"中层....");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"内层....");
}
}
}
},"Thread A").start();
}
public static void main(String[] args) {
new ReentrantLockDemo2().sychronizedMethod();
}
}
同步方法
public class ReentrantLockDemo2 {
public static void main(String[] args) {
new ReentrantLockDemo2().m1();
}
public synchronized void m1() {
System.out.println("===外");
m2();
}
public synchronized void m2() {
System.out.println("===中");
m3();
}
public synchronized void m3() {
System.out.println("===内");
}
}
通过以上代码可得知
在一个synchronized修饰的方法或者代码块内部,调用本类的其他synchronized修饰的方法或者代码块时,可以永远得到锁
深究其代码原理
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。
- 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。
- 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。
- 当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
显式锁(即Lock)也有ReentrantLock这样的可重入锁
public class ReentrantLockDemo{
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t get Lock");
setLock();
} finally {
lock.unlock();
}
},"Thread A").start();
}
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t get Lock");
setLock();
} finally {
lock.unlock();
}
},"Thread B").start();
}
}
}
通过上面的代码可得知
每个线程都要有入锁和出锁,不然在同一层面尚会出错
2.2 LockSupport
- 线程等待唤醒机制
- 创建锁和其他同步类的基本线程阻塞原语。park()和 unpark()的作用分别是阻塞线程和解除阻塞线程,比wait/notify,await/signal更强