面试题汇总
Sychnorized和volatile的区别
首先需要理解线程安全的两个方面:执行控制(Sychnorized)和内存可见(volatile)。
volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
Java中Thread类的start()方法和run()方法区别,分别直接调用会怎样
start():
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。run():
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。总结
调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。线程的几种状态及状态的切换
new 新建状态(此时线程刚被new出来,还没执行start()方法)
Runable 运行状态,有执行资格又有执行权,但它可能正在等待来自操作系统的其他资源,例如处理器。
Blocked 阻塞状态,也称临时状态,有执行资格,但是没有执行权,需要等待执行权
冻结状态,又分为Waiting(无限期等待)和Timed_Waiting(有限期等待)
Waiting 无限期等待状态 是用wait()
,线程自己不会醒,需要用notify方法或者其他方法来唤醒
Timed_Waiting 有限期等待状态 是用sleep()
传入一个要睡眠的时间,时间到了以后线程自己会醒冻结状态的线程是没有执行资格,更没有执行权
TERMINATED 终止消亡状态:表示该线程已经执行完毕。
wait()和sleep()区别
wait()来自Object类,sleep()来自Thread类
调用 sleep()方法,线程不会释放对象锁。而调用 wait() 方法线程会释放对象锁;
sleep()睡眠后不出让系统资源,wait()让其他线程可以占用 CPU;
sleep(millionseconds)需要指定一个睡眠时间,时间一到会自然唤醒。而wait()需要配合notify()或者notifyAll()使用
Java中”强软弱虚”四种引用的理解,分别适用于什么场合
强引用 正常创建的对象,只要引用存在,永远不会被GC回收,即使OOM
软引用 内存溢出之前进行回收,GC时内存不足时回收,如果内存足够就不回收,使用场景:在内存足够的情况下进行缓存,提升速度,内存不足时JVM自动回收
弱引用 每次GC时回收,无论内存是否足够都会回收,使用场景:1. ThreadLocalMap防止内存泄漏 2. 监控对象是否将要被回收
虚引用 每次垃圾回收时都会被回收,主要用于监测对象是否已经从内存中删除
类加载顺序(类中含静态变量、静态代码块、私有变量、构造方法,其中继承的父类也含静态变量、静态代码块、私有变量、构造方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53public class Main {
public static void main(String[] args) {
A ab = new B(1);
}
}
class A{
private static int A;
private int A1;
static{
System.out.println("A的静态变量"+A);
System.out.println("A我是静态代码块");
}
{
System.out.println("A1的私有变量"+A1);
System.out.println("A我是非静态代码块");
}
public A(){
System.out.println("A我是无参构造方法");
}
public A(int i){
System.out.println("A我是有参构造方法");
}
}
class B extends A{
private static int B;
private int B1;
static{
System.out.println("B的静态变量"+B);
System.out.println("B我是静态代码块");
}
{
System.out.println("B1的私有变量"+B1);
System.out.println("B我是非静态代码块");
}
public B(){
System.out.println("B我是无参构造方法");
}
public B(int i){
System.out.println("B我是有参构造方法");
}
}父类静态变量 –> 父类静态代码块 –> 子类静态变量 –> 子类静态代码块 –> 父类私有变量 –> 父类非静态代码块 –> 父类无参构造方法 –> 子类私有变量 –> 子类非静态代码块 –> 子类无参构造方法
假如父类中存在有参的构造方法:
父类静态变量 –> 父类静态代码块 –> 子类静态变量 –> 子类静态代码块 –> 父类私有变量 –> 父类非静态代码块 –>父类有参构造方法
–> 子类私有变量 –> 子类非静态代码块 –>子类有参构造方法