【面试问题】final 和可以保证可见性吗?

简介: 【1月更文挑战第27天】【面试问题】final 和可以保证可见性吗?

final 关键字在Java中具有多重作用,其中之一是在多线程环境下提供了可见性保证。虽然 final 本身并不是专门设计用来解决多线程可见性问题的,但是在一些特定的场景中,使用 final 可以达到保证可见性的效果。

final 与可见性的关系:

  1. 不可变对象:
  • 当一个对象被声明为 final 类型时,意味着该对象的引用和状态都是不可变的。由于不可变性,当一个线程对这个对象进行初始化后,其他线程获取到这个对象的引用时,可以确保看到的是初始化完成后的状态,因为不会发生对象状态的改变。
publicclassImmutableObject {
privatefinalintvalue;
publicImmutableObject(intvalue) {
this.value=value;
    }
publicintgetValue() {
returnvalue;
    }
}

在上述例子中,ImmutableObject 类的实例是不可变的,即一旦初始化后,其状态不会发生改变。如果一个线程在构造函数中初始化了该对象,其他线程获取到这个对象的引用后,可以确保看到的是初始化完成后的状态。

  1. final 字段:
  • 当一个字段被声明为 final 时,对这个字段的写操作将在构造函数执行完成之前完成,并且对其他线程来说,这个 final 字段的值是可见的。
publicclassFinalFieldExample {
privatefinalintfinalField;
publicFinalFieldExample() {
// 对 finalField 的写操作finalField=42;
    }
publicintgetFinalField() {
// 对 finalField 的读操作returnfinalField;
    }
}

在上述例子中,finalField 被声明为 final,并在构造函数中进行了初始化。在构造函数执行完成后,其他线程获取到 FinalFieldExample 实例的引用后,可以确保看到的是 finalField 的最终值。

final 与内存屏障:

在Java内存模型中,final 修饰的字段具有一些特殊的语义,可以被看作是一种轻量级的内存屏障。在Java 5及之后的版本中,对 final 字段的写操作会在构造函数结束前插入一个 StoreStore 屏障,保证了 final 字段的写入操作对于后续读操作的可见性。

这个特性意味着在构造函数中对 final 字段的写入操作对于其他线程来说是可见的,避免了对象的未完全构造状态被其他线程访问的情况。

使用 final 的注意事项:

  1. 对象引用的可见性:
  • 对象引用的 final 修饰仅保证引用本身的不可变性,而不保证引用对象的状态不可变。如果引用对象是可变的,其他线程仍然可以修改其状态。在使用 final 时,确保对象的状态也是不可变的才能更好地保证可见性。
publicclassMutableObject {
privateintvalue;
publicMutableObject(intvalue) {
this.value=value;
    }
publicintgetValue() {
returnvalue;
    }
publicvoidsetValue(intvalue) {
this.value=value;
    }
}
publicclassFinalReferenceExample {
privatefinalMutableObjectmutableObject;
publicFinalReferenceExample(MutableObjectmutableObject) {
this.mutableObject=mutableObject;
    }
publicMutableObjectgetMutableObject() {
returnmutableObject;
    }
}

在上述例子中,FinalReferenceExample 类包含一个 final 引用 mutableObject,但 mutableObject 对象本身是可变的,其他线程仍然可以修改其状态。

  1. 构造函数中的 final 字段:
  • 如果在构造函数中对 final 字段进行了写操作,并且其他线程在构造函数执行期间就能获取到该对象的引用,那么其他线程可能看到的是 final 字段的初始值而非最终值。
publicclassIncorrectFinalExample {
privatefinalintfinalField;
publicIncorrectFinalExample() {
// 对 finalField 的写操作finalField=42;
// 其他线程可能在这里获取到对象引用    }
publicintgetFinalField() {
// 其他线程可能看到 finalField 的初始值而非最终值returnfinalField;
    }
}

在上述例子中,由于其他线程可能在构造函数执行期间获取到对象引用,因此它们可能看到的是 finalField 的初始值而非最终值。在使用 final 时,确保在构造函数中对 final 字段的写操作发生在其他线程获取到对象引用之后。

相关文章
|
4月前
|
安全 Java 编译器
【面试问题】说说原子性、可见性、有序性?
【1月更文挑战第27天】【面试问题】说说原子性、可见性、有序性?
|
20天前
|
缓存 Java 编译器
面试官:volatile如何保证可见性的,具体如何实现?
面试官:volatile如何保证可见性的,具体如何实现?
23 0
|
7月前
|
Java API
每日一道面试题之final、finally、finalize 有什么区别?
每日一道面试题之final、finally、finalize 有什么区别?
|
10月前
|
Java 编译器
【Java面试】为什么匿名内部类只能访问外部类的final类型局部变量?
【Java面试】为什么匿名内部类只能访问外部类的final类型局部变量?
101 0
|
9月前
|
安全 Java
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
|
Java 数据库连接
Java 最常见的面试题:hibernate 实体类可以被定义为 final 吗?
Java 最常见的面试题:hibernate 实体类可以被定义为 final 吗?
76 0
Java 最常见的面试题:final、finally、finalize 有什么区别?
Java 最常见的面试题:final、finally、finalize 有什么区别?
|
Java
Java 最常见的面试题:final 在 java 中有什么作用
Java 最常见的面试题:final 在 java 中有什么作用
80 0
|
安全 Java 编译器
[Java基础面试题一]深入谈谈final、finally、 finalize 有什么不同?吊打面试官
[Java基础面试题一]深入谈谈final、finally、 finalize 有什么不同?吊打面试官
|
安全 IDE Java
【面试精讲】Java:final、finally 和 finalize 有什么区别?
Java 语言有很多看起来很相似,但是用途却完全不同的语言要素。快来看博主如何理解经典面试问题:谈谈 final、finally、 finalize 有什么不同?
158 0