java面试篇
2022-07-21 09:16:40 8 举报
AI智能生成
阿萨德
作者其他创作
大纲/内容
Java锁
synchronized得原理
针对方法
针对同步方法,看字节码可以发现的标志位有一个ACC_synchroniezd标志,有线程要访问方法的时候,会检查方法上是否有ACC_synchronized标志,如果有设置,在执行方法之前需要先获得监视器锁,并给对象的计数器+1,在方法执行完之后再将对象的计数器-1释放监视器锁,这样其他线程就可以获得锁了
针对代码块
针对synchronized修改代码块的时候会看到两个字节码,可以发现他有两个指令,monitorenter,monitorexit当JVM运行到monitorenter时,会拿到监视器锁并且给对象头里面的计数器+1,表示当前线程获得锁,当本线程再次尝试获得锁对象时,只需将计数器+1,我们知道synchronized时可重入锁,就是靠这个对象里头的计数器来实现的,当jvm执行到monitorexit的时候将锁对象计数器-1计数器为0的时候,代表锁将被释放,其他线程可以获得锁
Synchronized保证原子性、有序性、可见性原理
原子性
原子性的意思就是说,执行时不被其他因素干扰,并且结果值是正确,被synchronized修饰的代码.只能被一个线程执行,如果发生CPU时间片用完的情况,因为锁对象的可重入性,也会使得线程获得CPU时间片继续执行,能保证结果不被其他因素干扰
有序性
如果一条线程里面看所有的操作执行都是有序,但是如果从另外一条线程看的话,其他线程里面的操纵指令都是无须的,因为编译器会对代码进行重新排序,用synchronized修饰的代码只能被一条xian线程访问,所以说对于一条线程是有序的
可见性
因为synchronized修饰大的代码只能被一条线程执行,并且在代码执行完释放锁之前,会把线程本地的数据回写到主存,从而保证可见性
Synchronized和Lock的区别
sync会主动释放锁,lock不会,而且容易产生死锁
synchronized是关键字,lock是一个接口
synchronized是关键字,lock是一个接口
:synchronized假设线程A获取到锁,B线程就会等待,如果A线程阻塞B线程会一直等待
:lock锁的情况是不一样的 因为他是接口,
lock():获取锁,如果锁被暂用则一直等待
unlock():释放锁
tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false否则true
tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间
lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事
:lock锁的情况是不一样的 因为他是接口,
lock():获取锁,如果锁被暂用则一直等待
unlock():释放锁
tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false否则true
tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间
lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事
synchronized无法判断锁得状态
lock锁可以判断锁得状态
lock锁可以判断锁得状态
sychronized和ReenteredLock区别
synchronized 是JVM层面的锁,是Java关键字,通过monitor(妈辣der)对象来完成(monitorenter与monitorexit),对象只有在同步块或同步方法中才能调用wait/notify方法synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、向OS申请重量级锁,
ReentrantLock 实现则是通过利用CAS 自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能
ReentrantLock 实现则是通过利用CAS 自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能
synchronized不需要手动释放锁
死锁是什么?遇到死锁问该怎么解决?
多个线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放。
破坏不剥夺条件 :占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
JVM
常用的JVM调优的参数有哪些?
-Xms2g:初始化堆大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息
什么是双亲委派模型
一个类收到加载请求时,会先委派给父类去加载,父类无法加载再反馈给子类加载。
堆和栈的区别
堆 栈
物理地址 不连续,性能慢 连续(先进后出)性能快
内存分配 大小不固定,在运行期确认 大小固定,在编译器确认
存放内容 对象的实例和数组 局部变量、操作数栈、返回结果
程序可见度 整个应用可见 线程可见
物理地址 不连续,性能慢 连续(先进后出)性能快
内存分配 大小不固定,在运行期确认 大小固定,在编译器确认
存放内容 对象的实例和数组 局部变量、操作数栈、返回结果
程序可见度 整个应用可见 线程可见
说一下JVM运行时数据区
包含程序计数器、Java虚拟机栈、本地方发栈、Java堆、方法区
程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;
Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;
方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
Ps:jdk1.7之前常量池在方法区,1.7之后在堆里边。
程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;
Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;
方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
Ps:jdk1.7之前常量池在方法区,1.7之后在堆里边。
JVM的主要组成部分及作用
两个子系统:类加载子系统、执行引擎
两个组件:运行时数据区、本地库接口
作用(类执行过程):首先类加载子系统把编译好的.class文件加载到内存中,放到运 行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在 方法区内的数据结构。然后通过执行引擎将字节码翻译成底层系统指令交由CPU 去执行,这个过程中需要调用其他语言版本的本地库接口实现。
两个组件:运行时数据区、本地库接口
作用(类执行过程):首先类加载子系统把编译好的.class文件加载到内存中,放到运 行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在 方法区内的数据结构。然后通过执行引擎将字节码翻译成底层系统指令交由CPU 去执行,这个过程中需要调用其他语言版本的本地库接口实现。
JVM有哪些垃圾回收算法
标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
多线程
创建线程有哪几种方式
继承Thread类
多线程中继承Thread 类和实现Runnable 接口的区别
由于Java是单继承,一个类继承Thread类以后不能继承其他类,扩展性不好
而实现Runnable接口则可以侧面实现了多继承
实现Runnable接口
多线程中实现Runnable和Callable的接口的区别
Runnable接口中的唯一抽象方法run()方法没有返回值,callable接口中的唯一抽象方法call()是由返回值的
Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
Runnable接口的run方法只能在内部捕获异常,不能继续上抛,Callable方法可以直接抛出Exception异常,
实现Callable接口
通过过线程池的方式
线程池
线程池的好处
节省了线程池的创建和销毁带来的性能影响
控制线程池的并发数
线程池的参数
corePoolSize核心线程数
queueCapacity任务队列容量
maxPoolSize最大线程数
keepAliveTime线程空闲时间
allowCoreThreadTimeout允许核心线程超时
rejectedExecutionHandler任务拒绝处理器
线程池中 submit() 和 execute() 方法有什么区别?
1、excutor没有返回值,submit有返回值,并且返回执行结果Future对象
2、excutor不能提交Callable任务,只能提交Runnable任务,submit两者任务都可以提交
3、在submit中提交Runnable任务,会返回执行结果Future对象,但是Future调用get方法将返回null(Runnable没有返回值)
一个线程的生命周期有哪几种状态?它们之间如何流转的?
New(新创建)
Runnable(可运行)
Blocked(被阻塞)
Waiting(等待)
Timed Waiting(计时等待)
Terminated(被终止)
Runnable(可运行)
Blocked(被阻塞)
Waiting(等待)
Timed Waiting(计时等待)
Terminated(被终止)
阻塞
当线程进入synchronized代码块中没有拿到相应的锁就会导致阻塞
Waiting(等待)
当线程中调用了没有设置 Timeout 参数的 Object.wait() 方法,.join() 方法
Timed Waiting(计时等待)
线程执行了设置了时间参数的 Thread.sleep(long millis) 方法;
可以通过getState()方法来获取当前线程的状态
Thread类中有一个State枚举定义了这些方法
Thread类中有一个State枚举定义了这些方法
启动线程方法 start和 run有什么区别?
当程序调用start 方法的时候,将会去创建一个新的线程去执行run()方法中的代码,但是如调用run方法的时话,会执行执行当前线程中的run()方法中的代码,
并且当一个线程启动后不能重复调用start()方法,如果重复调用会报异常,但是可以重复调用run()方法
并且当一个线程启动后不能重复调用start()方法,如果重复调用会报异常,但是可以重复调用run()方法
线程的基本方法有哪些?
线程等待wait
线程睡眠
线程让步
线程中断
等待其他线程终止
线程唤醒
线程睡眠
线程让步
线程中断
等待其他线程终止
线程唤醒
线程中的 wait和 sleep方法有什么区别?
wait()方法
1、属于Object,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程
2、wait方法释放了锁
3、wait不需要捕获异常
4、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用
sleep()方法
1、属于Thread类,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态
2、sleep方法没有释放锁
3、sleep必须捕获异常
4、sleep可以在 任何地方使用
Linux命令
pwd
查看当前目录的完整路径
kill
强制杀死进程
rpm -ivh 软件名称
: 安装指定的软件
ll
用来查看当前目录下的所有文件资源。
clear
清屏
esc:wq! 强制保存并退出linux常用命令
unzip 文件名.zip :解压后缀名为zip的压缩文件
ps -ef | grep 进程名 :查看指定进程是否启动。
简答:
BIO:Block IO 同步阻塞式IO,就是我们平时使用的传统IO,特点是模式简单使用方便,但是并发处理能力低;
NIO:Non IO 同步非阻塞IO,传统IO的升级,客户端与服务端通过Channel(通道)通讯,实现了多路复用;
AIO:Asynchronous IO 异步非阻塞IO,异步IO的操作基于事件和回调机制。
BIO:Block IO 同步阻塞式IO,就是我们平时使用的传统IO,特点是模式简单使用方便,但是并发处理能力低;
NIO:Non IO 同步非阻塞IO,传统IO的升级,客户端与服务端通过Channel(通道)通讯,实现了多路复用;
AIO:Asynchronous IO 异步非阻塞IO,异步IO的操作基于事件和回调机制。
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
0 条评论
下一页