静态方法类被多线程调用安全性
Posted yuanxulong198010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了静态方法类被多线程调用安全性相关的知识,希望对你有一定的参考价值。
有如下测试类代码:
public class Test1 {
static int FACTOR = 2;
public static void main(String[] args) {
int c = add(2, 3);
}
public static int add(int a, int b) {
return a + b;
}
}
问题:
方法add在被多线程调用的时候有没有可能存在线程安全性的问题?
例如,线程1调用add,传入参数3,5,期望得到的答案是8,而线程2调用时传入的参数是4,9,期望得到答案是13,在多线程调用的环境中,会不会存在计算出错的可能?
答案:不可能。
原因:
要理解这个问题,首先要简单了解一下jvm的内存模型,如下:
然后运行javap -c -v Test1,得到如下java字节码(只留最主要的)
public class com.chaoxing.Test1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#25 // java/lang/Object."<init>":()V
#2 = Methodref #3.#26 // com/chaoxing/Test1.add:(II)I
#3 = Class #27 // com/chaoxing/Test1
#4 = Class #28 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#11 = Utf8 Lcom/chaoxing/Test1;
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 args
#15 = Utf8 [Ljava/lang/String;
#16 = Utf8 c
#17 = Utf8 I
#18 = Utf8 MethodParameters
#19 = Utf8 add
#20 = Utf8 (II)I
#21 = Utf8 a
#22 = Utf8 b
#23 = Utf8 SourceFile
#24 = Utf8 Test1.java
#25 = NameAndType #5:#6 // "<init>":()V
#26 = NameAndType #19:#20 // add:(II)I
#27 = Utf8 com/chaoxing/Test1
#28 = Utf8 java/lang/Object
{
public com.chaoxing.Test1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/chaoxing/Test1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_2
1: iconst_3
2: invokestatic #2 // Method add:(II)I
5: istore_1
6: return
LineNumberTable:
line 8: 0
line 9: 6
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 args [Ljava/lang/String;
6 1 1 c I
MethodParameters:
Name Flags
args
public static int add(int, int);
descriptor: (II)I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=2
0: iload_0
1: iload_1
2: iadd
3: ireturn
LineNumberTable:
line 12: 0
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b I
MethodParameters:
Name Flags
a
b
}
看一下调用过程,
1.iconst_2 ,主函数main先把常量2压栈。
2. iconst_3,把常量3压栈。
3. invokestatic #2,调用add方法,调用方法的时候,会创建一个栈帧(frame),栈帧组成结构如下图:
然后从main方法中的操作数栈中弹出与被调用方法add的参数(此处是常量2,常量3),写入到add栈帧中的本地变量表中,并把add方法的栈帧标记为当前栈帧,此时线程的栈如下图(只是形象表示):
4. 此时开始调用add方法
4.1:iload_0,从本地变量表中把第一个变量添加到操作数栈中(此处是常量2)。
4.2:iload_1,从本地变量表中把第二个变量添加到操作数栈中(此处是常量3)。
4.3:iadd,进行相加操作,该方法从操作数中弹出第一个变量和第二个变量,并进行相加操作,然后把结果写入到操作数栈中。
4.4:ireturn:i代表整型值的返回,该方法从操作数栈中弹出一个数并写入到调用者的操作数栈中(此处是main方法的操作数栈)。
5. add方法结束,销毁add方法的栈帧,把当前栈帧指向main方法的栈帧,并且继续main方法的执行。
6. istore_1:从操作数中弹出一个整型值,此时是add方法返回的值5,并且把该值写入到本地变量表中。
7. return:程序结束。
在程序执行的过程中,每一个线程都会有一个自己的堆栈(stack),在本例中,因为只有main线程调用了一个add方法,不考虑jvm内部其他方法调用,因此我们理解为只有一个堆栈,如果启动了多线程,那么每个线程都会有自己的堆栈,因此就算在多线程调用的过程中,因为堆栈是私有的,所以方法中的变量或者参数都是私有的,不存在互相冲突导致计算结果错误的问题,但是如果在方法中使用了可变的全局变量,则要注意存在线程安全问题。
以上是关于静态方法类被多线程调用安全性的主要内容,如果未能解决你的问题,请参考以下文章