面试题汇总

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
    53
    public 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我是有参构造方法");
    }
    }
  • 父类静态变量 –> 父类静态代码块 –> 子类静态变量 –> 子类静态代码块 –> 父类私有变量 –> 父类非静态代码块 –> 父类无参构造方法 –> 子类私有变量 –> 子类非静态代码块 –> 子类无参构造方法

  • 假如父类中存在有参的构造方法:
    父类静态变量 –> 父类静态代码块 –> 子类静态变量 –> 子类静态代码块 –> 父类私有变量 –> 父类非静态代码块 –> 父类有参构造方法 –> 子类私有变量 –> 子类非静态代码块 –> 子类有参构造方法