2022-04-03 面试题:synchronized 部分原理+常量池部分问题+自动装箱/http 部分问题

2022-04-03 synchronized 部分原理+常量池部分问题+自动装箱/http 部分问题

部分整理参考来自
https://blog.csdn.net/zzti_erlie/article/details/86587263?utm_source=app&app_version=5.3.0
https://blog.csdn.net/zzti_erlie/article/details/103997713?utm_source=app&app_version=5.3.0

新十道

  1. synchronized关键字可以实现什么类型的锁?
    jdk1.6 版本以后,锁的升级使得 synchronized 得到了一个质的优化。实现锁的种类也更加丰富
    原本 synchronized 实现的锁:
  • 悲观锁:synchronized关键字实现的是悲观锁,每次访问共享资源时都会上锁。
  • 非公平锁:synchronized关键字实现的是非公平锁,即线程获取锁的顺序并不一定是按照线程阻塞的顺序。
  • 独占锁或者排他锁:synchronized关键字实现的是独占锁,即该锁只能被一个线程所持有,其他线程均被阻塞。
    1.6 之后增加:
  • 可重入锁:synchronized关键字实现的是可重入锁,即已经获取锁的线程可以再次获取锁。
  • 轻量级锁:当短时间,线程竞争较少的情况下,会优先启动轻量级锁,效率更高
  • 偏向锁:某一线程拥有synchronized 锁对象后,synchronized 会偏向于该线程,使得下次该线程获得锁更容易。
  1. 偏向锁原理和升级过程
    偏向锁过程:当线程1访问代码块并获取锁对象时,使用 cas 加锁成功时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,线程一再次主动获取该锁时,则无需使用CAS来加锁、
    解锁:
  • 改变偏向:如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程可以改变偏向。如果线程1 不再使用该锁对象,且线程 2 多次索取该锁 那么将锁对象状态设为无锁状态,重新偏向新的线程。
  • 锁升级:如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,
  1. 为什么要引入轻量级锁?

轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,所以在几个线程轮流来获取锁的情况下,使用轻量级锁就足够了。

  1. 轻量级锁原理和升级过程

线程1获取轻量级锁时会先把锁对象的对象头MarkWord复制一份到线程1的栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录(DisplacedMarkWord)的地址;此时完成的偏向锁的上锁。

此时如果线程 2 来竞争锁,进行 cas 获取锁对象上的锁记录地址,但是发现已经被线程一占用了,此时他就会进行锁自旋。(不断 cas)

  • 如果在自旋过程中,线程一放弃了对锁的占用,线程二获取锁,锁变为轻量级锁
  • 而自旋是有次数限制的,自旋到了还没有释放锁,就会发生锁膨胀,编程重量级锁,将所有没拥有锁的线程都放到阻塞队列里。
  1. 为什么要有常量池?
    常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
    例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
    (1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
    (2)节省运行时间:比较字符串时,== 比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
  2. 关于常量池的一些问题
  • Java有8种基本数据类型
    整数类型:byte,short,int,long。包装类型为Byte,Short,Integer,Long
    浮点类型:float、double。包装类型为Float,Double
    字符类型:char。包装类型为Character
    布尔类型:boolean。包装类型为Boolean
    8种包装类型中除了Float,Double没有实现常量池,剩下的都实现了
  • 什么是自动装箱,自动拆箱
    自动装箱指的就是将原始的 java 类值变为一个对象。自动拆箱与其相反。
  • Integer 类常量池引发的问题
    主要的问题是,Integer 在[-128,127]里对象是直接取自常量池的。
public static Integer valueOf(int i) {
    // i的取值范围为[-128,127]
	if (i >= IntegerCache.low && i <= IntegerCache.high)
		return IntegerCache.cache[i + (-IntegerCache.low)];
	return new Integer(i);
}

而超过这个范围,就要创建一个新的对象。
所以:

Integer a1 = 40;
Integer a2 = 40;
// true
System.out.println(a1 == a2);相等
Integer a3 = 200;
Integer a4 = 200;
// false
System.out.println(a3 == a4);//不相等
  • String 常量池
    有这样个问题:
String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc");
// true
System.out.println(str1 == str2);
// false
System.out.println(str1 == str3);
// false
System.out.println(str3 == str4);

解释是这样的:

采用字面值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在"abc"这个对象

如果不存在,则在字符串池中创建"abc"这个对象,然后将池中"abc"这个对象的地址赋给str1,这样str1会指向池中"abc"这个字符串对象

如果存在,则不创建任何对象,直接将池中"abc"这个对象的地址返回,赋给str2。因为str1、str2指向同一个字符串池中的"abc"对象,所以结果为true。
而如果是 new 的:
无论常量池有没有都在堆里创建,常量池没有的话给常量池也创建一个对应的。
采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"abc"这个字符串对象,

如果没有,则首先在字符串池中创建一个"abc"字符串对象,然后再在堆中创建一个"abc"字符串对象,然后将堆中这个"abc"字符串对象的地址赋给str3

如果有,则不在池中再去创建"abc"这个对象了,直接在堆中创建一个"abc"字符串对象,然后将堆中的这个"abc"对象的地址赋给str4。这样,str4就指向了堆中创建的这个"abc"字符串对象;

复习五道

  1. 什么是HTTP报文?
    Http报文就是客户端和服务端之间传送的数据块

  2. HTTP报文由哪三部分组成?
    HTTP报文由起始行,头部,主体组成,其中起始行是对该报文进行的描述,头部是对报文的属性进行的描述,主体则是数据的内容。

  3. HTTP报文分为哪两类?
    HTTP报文分为请求报文和响应报文,当客户端给服务器发送请求时,发送的报文就是请求报文,当服务器端給客户端返回请求时,返回的请求就是响应报文。

  4. HTTP常见的请求方法有哪些?
    常见的请求方式有
    get请求:向服务器端发送报文,
    post请求:向服务器端发送待处理的数据请求,
    HEAD请求:获取服务器端制定的报文头部信息,
    PUT请求:向服务器端发送请求并且更改制定的信息,
    DELETE请求:删除指定的报文数据

  5. HTTP的状态码分为哪几类?
    100-101(2个)代表有提示信息,
    200-206:代表请求成功 ,
    300-305:重定向或者转发
    400-415:客户端方出现了错误
    500-505 :服务端出现了错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值