【面试问题】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 字段的写操作发生在其他线程获取到对象引用之后。

相关文章
|
6月前
|
安全 Java 编译器
【面试问题】说说原子性、可见性、有序性?
【1月更文挑战第27天】【面试问题】说说原子性、可见性、有序性?
|
1月前
|
Java 程序员
Java 面试高频考点:static 和 final 深度剖析
本文介绍了 Java 中的 `static` 和 `final` 关键字。`static` 修饰的属性和方法属于类而非对象,所有实例共享;`final` 用于变量、方法和类,确保其不可修改或继承。两者结合可用于定义常量。文章通过具体示例详细解析了它们的用法和应用场景。
28 3
|
3月前
|
存储 Java 对象存储
【Java基础面试四十三】、 static和final有什么区别?
由于网络原因,我无法获取到您提供的链接内容。如果需要我解析该网页,请确保链接有效并重试,或者提供其他问题,我会尽力帮助您。
|
4月前
|
缓存 安全 Java
Java面试题:解释volatile关键字的作用,以及它如何保证内存的可见性
Java面试题:解释volatile关键字的作用,以及它如何保证内存的可见性
77 4
|
4月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
78 1
|
4月前
|
安全 Java 开发者
Java面试题:Java内存模型解析,Java内存模型的基本概念和它的重要性,Java内存模型中的“可见性”和“有序性”,以及具体实现?
Java面试题:Java内存模型解析,Java内存模型的基本概念和它的重要性,Java内存模型中的“可见性”和“有序性”,以及具体实现?
56 1
|
4月前
|
存储 缓存 安全
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
36 0
|
4月前
|
Java 开发者
Java面试题:解释Java内存模型中的内存可见性,解释Java中的线程池(ThreadPool)的工作原理,解释Java中的CountDownLatch和CyclicBarrier的区别
Java面试题:解释Java内存模型中的内存可见性,解释Java中的线程池(ThreadPool)的工作原理,解释Java中的CountDownLatch和CyclicBarrier的区别
26 0
|
4月前
|
存储 安全 Java
Java面试题:Java内存模型中的主内存与工作内存是如何协同工作的?请解释Java内存模型中的可见性、原子性和有序性,举例说明Java内存模型中的happens-before关系
Java面试题:Java内存模型中的主内存与工作内存是如何协同工作的?请解释Java内存模型中的可见性、原子性和有序性,举例说明Java内存模型中的happens-before关系
36 0
|
6月前
|
Java
【JAVA面试题】final关键字的作用有哪些
【JAVA面试题】final关键字的作用有哪些