21.synchronized使用有哪些注意点?
- 锁对象不能为空,因为锁是保存在对象头中,对象都没有,就没有对象头
- 作用域不能过大,把所有代码都被修饰,就是串行了,会影响到程序执行的速度
- 避免死锁,两个锁,两个线程,线程一持有锁一请求锁二,线程二持有锁二请求锁一
22.如何选择Lock和synchronized关键字?
- 如何可以的话,不使用这两个,尽量使用JUC包中的类
- 优先使用synchronized,可以少写代码
- 特别需要Lock的特性的时候才使用Lock,比如:灵活加锁释放锁机制
23.多线程访问同步方法的各种具体情况
- 两个线程同时访问一个对象的同步方法:会发生同步,锁对象都为同一个实例对象;
- 两个线程同时访问两个对象的同步方法:互不影响,锁对象不同;
- 两个线程访问的是synchronized的静态方法:会发生同步,锁对象都为Class对象,Class对象只有一个;
- 同时访问同步方法和非同步方法:非同步方法不受影响,不发生同步;
- 访问同一个对象不同的普通同步方法:会发生同步,锁对象默认为同一个实例对象;
- 同时访问静态synchronized和非静态synchronized方法:互不影响,静态syn方法的锁对象为Class对象,非静态syn方法的锁对象为一个实例对象this,实例对象和Class对象不是同一个对象,实例对象在堆中,Class对象在方法区中;
- 方法抛出异常后,会释放锁
24.多个线程等待同一个sync锁的时候,JVM如何选择获取锁的下一个线程?用的什么算法呢?
处于不公平的状态,和JVM的版本和具体实现都有关系,判定是随机的。
25.sync使得同时只有一个线程执行代码,性能较差,有什么办法可以提升性能?
- 优化使用范围,要被保护的区域,满足业务需求的情况下,尽可能的小
- 使用其它类型的Lock,比如读写锁,读不用线程安全,写的时候保证线程安全
26.我想灵活的控制锁的获取和释放,而现在释放锁的时机都被规定死了,怎么办?
自己实现Lock的接口
27.一句话总结synchronized?
JVM通过使用monitor,加减monitor计数器来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质。
28.什么是锁的升级和降级?什么是JVM里的轻量级锁,重量级锁,偏斜锁?
JVM利用关键字使用的次数,来对锁进行优化,升级和降级:把轻量级锁升级到重量级锁等等
29.单例模式的作用及使用场景?
作用:
- 节省内存和计算:如果构造函数中有许多资源需要初始化,每次new都需要初始化,十分耗时,单例只需初始化一次;
- 保证结果正确:多线程进行统计操作时,需要一个全局的计数器,单例模式可保证计数器只有一个;
- 方便管理:一些常见的工具类可以用单例模式管理
使用场景:
- 工具类:比如日志工具类,格式转换工具类等,不管在哪里使用,都是无状态的,只需要一个实例对象即可;
- 全局信息类:比如全局的计数器,统计网站的全局访问次数;
30.单例模式的各种写法?
/**
* 饿汉式
*/
public class HungerSingleton {
private static final HungerSingleton instance = new HungerSingleton();
private HungerSingleton() {}
public static HungerSingleton getInstance() {
return instance;
}
}
/**
* 懒汉式,双重检查锁,防止反射
*/
public class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton instance;
private DoubleCheckSingleton() {
if (instance != null) {
throw new RuntimeException("不允许重复创建实例");
}
}
public DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
/**
* 饿汉式,静态内部类,防止反射
*/
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
if (StaticInnerClass.instance != null) {
throw new RuntimeException("不允许重复创建实例");
}
}
public StaticInnerClassSingleton getInstance() {
return StaticInnerClass.instance;
}
private static final class StaticInnerClass {
private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
}
}
31.哪种单例模式最好?
枚举最好,写法简单,延迟加载,线程安全,防止反射和序列化
32.饿汉式、懒汉式各有什么缺点?
饿汉式:一开始就加载,浪费内存,同时如果需要配置文件配置话,没法去动态调整加载的内容;
懒汉式:写法复杂,线程不安全;
33.为什么用双重检查锁?
线程安全,延迟加载,volatile关键字可保证创建对象的时候能禁止重排序
34.什么是Java内存模型?
- 起因:不同CPU性能不同,不用JMM的话,需要我们手动同步;
- 定义:一组规范,规范CPU和代码之间一系列的转换关系;
- 重排序;
- 可见性:主内存和本地内存,happens-before原则,volatile:禁止重排序,可见性,近朱者赤,轻量级的synchronized,适用于赋值操作,没有判断和累加操作;
- 原子性:synchronized:加锁和释放锁原理,近朱者赤;原子:基本类型赋值,引用赋值,原子包;
35.什么是原子操作,Java中有哪些原子操作,生成对象的过程是不是原子操作?
- 一系列的操作,要么成功,要么都失败;
- 基本类型和引用赋值,原子包;
- 生成对象:创建空对象,调用构造方法,引用赋值,不是原子操作;
36.什么是内存可见性?
37.64位的double和long写入的时候是原子的吗?
32位JVM上有可能出现前32和后32错位的情况,64位JVM是原子的,商用JVM不需要。