【Java】几道让你拿offer的面试题(一)

简介: 笔记

前言


之前在刷博客的时候,发现一些写得比较好的博客都会默默收藏起来。最近在查阅补漏,有的知识点比较重要的,但是在之前的博客中还没有写到,于是趁着闲整理一下。

文本的知识点:

  • Integer常量池
  • TCP拆包粘包
  • select、poll、epoll简单区别
  • jdk1.6以后对Synchronize锁优化
  • Java内存模型

本文力求简单讲清每个知识点,希望大家看完能有所收获


一、神奇的Integer


前阵子在群上看有人在讨论关于Integer的true或者false问题,我本以为我已经懂了这方面的知识点了。但还是做错了,后来去请教了一下朋友。朋友又给我发了另一张图:

1.jpg

后来发现这是出自《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》中的10.3.2小节中~

public class Main_1 {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        System.out.println(c == d);
        System.out.println(e == f);
        System.out.println(c == (a + b));
        System.out.println(c.equals(a + b));
        System.out.println(g == (a + b));
        System.out.println(g.equals(a + b));
        System.out.println(g.equals(a + h));
    }
}

你们可以先思考一下再往下翻看答案,看看能不能做对。


1.1解题思路


在解这道题之前,相信很多人都已经知道了,在Java中会有一个Integer缓存池,缓存的大小是:-128~127

2.jpg

答案是:

  • true
  • false
  • true
  • true
  • true
  • false
  • true

简单解释一下:

  • 使用==的情况:
  • 如果比较Integer变量,默认比较的是地址值
  • Java的Integer维护了从`-128~127`的缓存池
  • 如果比较的某一边有操作表达式(例如a+b),那么比较的是具体数值
  • 使用equals()的情况:
  • 无论是Integer还是Long中的`equals()`默认比较的是数值
  • Long的`equals()`方法,JDK的默认实现:会判断是否是Long类型
  • 注意自动拆箱,自动装箱问题。

3.jpg

反编译一下看看:

import java.io.PrintStream;
public class Main_1 {
    public static void main(String[] paramArrayOfString) {
        Integer localInteger1 = Integer.valueOf(1);
        Integer localInteger2 = Integer.valueOf(2);
        Integer localInteger3 = Integer.valueOf(3);
        Integer localInteger4 = Integer.valueOf(3);
        Integer localInteger5 = Integer.valueOf(321);
        Integer localInteger6 = Integer.valueOf(321);
        Long localLong = Long.valueOf(3L);
        // 缓存池
        System.out.println(localInteger3 == localInteger4);
        // 超出缓存池范围
        System.out.println(localInteger5 == localInteger6);
        // 存在a+b数值表达式,比较的是数值
        System.out.println(localInteger3.intValue() == localInteger1.intValue() + localInteger2.intValue());
        // equals比较的是数值
        System.out.println(localInteger3.equals(Integer.valueOf(localInteger1.intValue() + localInteger2.intValue())));
        // 存在a+b数值表达式,比较的是数值
        System.out.println(localLong.longValue() == localInteger1.intValue() + localInteger2.intValue());
        // Long的equals()先判断传递进来的是不是Long类型,而a+b自动装箱的是Integer类型
        System.out.println(localLong.equals(Integer.valueOf(localInteger1.intValue() + localInteger2.intValue())));
        // ... 最后一句在这里漏掉了,大家应该可以推断出来
    }
}

我使用的反编译工具是jd-gui,如果还没有试过反编译的同学可以下载来玩玩:


二、Synchronize锁优化手段有哪些


多线程文章回顾:

之前在写多线程文章的时候,简单说了一下synchronized锁在jdk1.6以后会有各种的优化:适应自旋锁,锁消除,锁粗化,轻量级锁,偏向锁。

4.jpg

本以为这些优化是非常难以理解的东西,其实不然~~~简单了解一下还是很好理解的。


2.1适应自旋锁


锁竞争是kernal mode下的,会经过user mode(用户态)到kernal mode(内核态) 的切换,是比较花时间的。

自旋锁出现的原因是人们发现大多数时候锁的占用只会持续很短的时间,甚至低于切换到kernal mode所花的时间,所以在进入kernal mode前让线程等待有限的时间,如果在此时间内能够获取到锁就避免了很多无谓的时间,若不能则再进入kernal mode竞争锁。

在JDK 1.6中引入了自适应的自旋锁,说明自旋的时间不固定,要不要自旋变得越来越聪明

自旋锁在JDK1.4.2中就已经引入,只不过默认是关闭的,可以使用-XX:+UseSpinning参数来开启,在JDK1.6中就已经改为默认开启了。

参考资料:


2.2锁消除


如果JVM明显检测到某段代码是线程安全的(言外之意:无锁也是安全的),JVM会安全地原有的锁消除掉!

比如说:

public void vectorTest(){
        Vector<String> vector = new Vector<String>();
        for(int i = 0 ; i < 10 ; i++){
            vector.add(i + "");
        }
        System.out.println(vector);
    }

Vector是默认加锁的,但JVM如果发现vector变量仅仅在vectorTest()方法中使用,那该vector是线程安全的。JVM会把vector内部加的锁去除,这个优化就叫做:锁消除。


2.3锁粗化


默认情况下,总是推荐将同步块的作用范围限制得尽量小

但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,频繁地进行互斥同步操作也会导致不必要的性能损耗

JVM会将加锁的范围扩展(粗化),这就叫做锁粗化。


2.4轻量级锁


轻量级锁能提升程序同步性能的依据是“对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。

  • 如果没有竞争,轻量级锁使用CAS操作避免了使用互斥量的开销
  • 但如果存在锁竞争,除了互斥量的开销外,还额外发生了CAS操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。

简单来说:如果发现同步周期内都是不存在竞争,JVM会使用CAS操作来替代操作系统互斥量。这个优化就被叫做轻量级锁。


2.5偏向锁


偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了

偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数-XX:-UseBiasedLocking来禁止偏向锁优化反而可以提升性能。


2.6简单总结各种锁优化


  • 自适应偏向锁:自旋时间不固定
  • 锁消除:如果发现代码是线程安全的,将锁去掉
  • 锁粗化:加锁范围过小(重复加锁),将加锁的范围扩展
  • 轻量级锁:在无竞争的情况下使用CAS操作去消除同步使用的互斥量
  • 偏向锁:在无竞争环境下,把整个同步都消除,CAS也不做。

参考资料:

目录
相关文章
|
2天前
|
Java
Java面向对象实践小结(含面试题)(下)
Java面向对象实践小结(含面试题)(下)
12 1
|
2天前
|
存储 缓存 开发框架
Java基础面试题小结(下)
Java基础面试题小结(下)
9 0
|
2天前
|
存储 安全 Java
Java基础面试题小结(上)
Java基础面试题小结(上)
13 0
|
2天前
|
设计模式 Java
Java面向对象实践小结(含面试题)(上)
Java面向对象实践小结(含面试题)
12 1
|
4天前
|
Java
【JAVA面试题】static的作用是什么?详细介绍
【JAVA面试题】static的作用是什么?详细介绍
|
4天前
|
Java
【JAVA面试题】final关键字的作用有哪些
【JAVA面试题】final关键字的作用有哪些
|
4天前
|
JavaScript 前端开发 Java
【JAVA面试题】什么是引用传递?什么是值传递?
【JAVA面试题】什么是引用传递?什么是值传递?
|
4天前
|
安全 Java
【JAVA面试题】什么是对象锁?什么是类锁?
【JAVA面试题】什么是对象锁?什么是类锁?
|
4天前
|
存储 自然语言处理 Java
【JAVA面试题】什么是代码单元?什么是码点?
【JAVA面试题】什么是代码单元?什么是码点?
|
4天前
|
Java 程序员
【JAVA面试题】基本类型的强制类型转换是否会丢失精度?引用类型的强制类型转换需要注意什么?
【JAVA面试题】基本类型的强制类型转换是否会丢失精度?引用类型的强制类型转换需要注意什么?