网站首页 > 精选教程 正文
final在Java中是一个保留的关键字,可以修饰变量、方法和类。那么fianl在并发编程中有什么作用呢?本文就在对final常见应用总结基础上,讲解final并发编程中的应用。
1. final基础应用
final变量
final变量只能被赋值一次,赋值后值不再改变。(final要求地址值不能改变)
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;
如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。
本质上是一回事,因为引用的值是一个地址,final要求地址值不发生变化。
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但**该引用所指向的对象的内容是可以发生变化的**。本质上是一回事,因为引用的值是一个地址,final要求地址值不发生变化。
final成员变量:两种初始化方式,一种是在变量声明的时候初始化;第二种是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
final方法
final修饰的方法在编译阶段被静态绑定(static binding),不能被重写。
final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。(注:类的private方法会隐式地被指定为final方法)
final类
final修饰的类不能被继承。
final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
关于final的几个重要知识点
final关键字可以提高性能,JVM和Java应用都会缓存final变量,JVM会对方法、变量及类进行优化。
在匿名类中所有变量都必须是final变量。
接口中声明的所有变量本身是final的。
final和abstract这两个关键字是反相关的,final类就不可能是abstract的。
按照Java代码惯例,final变量就是常量,而且通常常量名要大写。
final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
2. 并发编程中的final
2.1 写final域
在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
编译器会在final域的写之后,插入一个StoreStore屏障,这个屏障可以禁止处理器把final域的写重排序到构造函数之外。
解释:保证先写入对象的final变量,后调用该对象引用。
举例
public class FinalDemo {
private int a; // 普通域
private final int b; // final域
private static FinalDemo finalDemo;
publicFinalDemo {
a = 1; // ①写普通域
b = 2; // ②写final域
}
public static voidwriter {
// 两个操作:
// 1)构造一个FinalExample类型的对象,①写普通域a=1,②写final域b=2
// 2)③把这个对象的引用赋值给引用变量finalDemo
finalDemo = new FinalDemo;
}
public static voidreader {
FinalDemo demo = finalDemo; // ④读对象引用
int a = demo.a; // ⑤读普通域
int b = demo.b; // ⑥读final域
}
}
假设一个线程A执行writer方法,随后另一个线程B执行reader方法。通过这两个线程的交互来说明写final域的规则。下图是一种可能的执行时序:
写普通域的操作可以被编译器重排序到了构造函数,①写普通域和③把这个对象的引用赋值给引用变量finalDemo重排序,导致读线程B错误的读取了普通变量a的值。
写final域的操作不能重排序到了构造函数外,②写final域和③把这个对象的引用赋值给引用变量finalDemo不能重排序,读线程B正确的读取了final变量b的值。
2.2 读final域
初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。
编译器会在读final域操作的前面插入一个LoadLoad屏障,这个屏障可以禁止读对象引用和读该对象final域重排序。
解释:先读对象的引用,后读该对象的final变量。
举例:
还是上面那段代码,假设一个线程A执行writer方法,随后另一个线程B执行reader方法。下图是一种可能的执行时序:
读对象的普通域的操作可以被重排序到读对象引用之前,⑤读普通域与④读对象引用重排序,读普通域a时,a没有被写线程A写入,导致错误的读取。
读final域的操作不可以被重排序到读对象引用之前,④读对象引用和⑥读final域不能重排序,读取该final域b时已经被A线程初始化过了,不会有问题。
2.3 final域为引用类型
对于引用类型,写final域的重排序规则对编译器和处理器增加了如下约束:在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
解释:
注意是增加了一条约束,所以以上两条约束都还生效。
保证先写入对象的final变量的成员变量,后调用该对象引用。
举例:
public class FinalReferenceDemo {
final int arrays;
private FinalReferenceDemo finalReferenceDemo;
publicFinalReferenceDemo {
arrays = new int[1]; //1
arrays[0] = 1; //2
}
public voidwriterOne {
finalReferenceDemo = new FinalReferenceDemo; //3
}
public voidwriterTwo {
arrays[0] = 2; //4
}
public voidreader {
if (finalReferenceDemo != ) { //5
int temp = finalReferenceDemo.arrays[0]; //6
}
}
}
假设首先线程A执行writerOne方法,执行后线程B执行writerTwo方法,执行后线程C执行reader方法。下面是一种可能的线程执行时序:
1是对final域的写入,2是对这个final域引用的对象的成员域的写入,3是把被构造的对象的引用赋值给某个引用变量。
由写final域的重排序规则“写final域的操作不能重排序到了构造函数外”可知,1和3是不能重排序的。
引用类型final域的重排序规则“final引用的对象的成员域的写入不能重排序到了构造函数外”,保证了2和3不能重排序。所以线程C至少能看到数组下标0的值为1。
写线程B对数组元素的写入,读线程C不一定能看到。因为写线程B和读线程C之间存在数据竞争,此时的执行结果不可预知。
总结
final基础应用
final修饰的变量地址值不能改变。
final修饰的方法不能被重写。
final修饰的类不能被继承。
并发编程中final可以禁止特定的重排序。
final保证先写入对象的final变量,后调用该对象引用。
final保证先读对象的引用,后读该对象的final变量。
final保证先写入对象的final变量的成员变量,后调用该对象引用。
并发系列文章汇总
01 | 开篇获奖感言
02 | 并发编程三大核心问题
03 | 重排序-可见性和有序性问题根源
04 | Java内存模型详解
05 | 深入理解volatile
———— e n d ————
微服务、高并发、JVM调优、面试专栏等20 大进阶架构师专题请关注公众号【Java进阶架构师】后在菜单栏查看。
看到这里,说明你喜欢本文
你的转发,是对我最大的鼓励!在看亦是支持↓
猜你喜欢
- 2024-10-30 java 你用了这么久,真的明白static和final的区别么?
- 2024-10-30 JAVA笔记(十五)面向对象——关键字abstract、final
- 2024-10-30 「java面试_01」String类为什么是final
- 2024-10-30 Java编程思想:final 关键字 java中finally关键字
- 2024-10-30 Java八股文:final、finally、finalize之间有什么区别
- 2024-10-30 在java中String类为什么要设计成final?Java面试常见问题
- 2024-10-30 lambda中的外部局部变量为什么需要声明final?
- 2024-10-30 java笔记static和final用法 java static与final
- 2024-10-30 Java中为什么String类被定义成final
- 2024-10-30 Java中的final修饰符 java中final可以修饰接口吗
你 发表评论:
欢迎- 04-11Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- 04-11Java中你知道几种从字符串中找指定的字符的数量
- 04-11探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- 04-11Python字符串详解与示例(python字符串的常见操作)
- 04-11java正则-取出指定字符串之间的内容
- 04-11String s1 = new String("abc");这句话创建了几个字符串对象?
- 04-11java判断字符串中是否包含某个字符
- 04-11关于java开发中正确的发牌逻辑编写规范
- 最近发表
-
- Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- Java中你知道几种从字符串中找指定的字符的数量
- 探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- Python字符串详解与示例(python字符串的常见操作)
- java正则-取出指定字符串之间的内容
- String s1 = new String("abc");这句话创建了几个字符串对象?
- java判断字符串中是否包含某个字符
- 关于java开发中正确的发牌逻辑编写规范
- windows、linux如何后台运行jar(并且显示进程名)
- 腾讯大佬私人收藏,GitHub上最受欢迎的100个JAVA库,值得学习
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)