线程安全性
作者:互联网
- 线程安全
当多个线程访问某个类,不管运行时环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类为线程安全的
- 新建线程不安全操作的案例类
public class UnSafeThread {
private static int num = 0;
private static CountDownLatch countDownLatch = new CountDownLatch(10);
/**
* 每次调用对num进行++操作
*/
public static void inCreate() {
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 100; j++) {
inCreate();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//每个线程执行完成之后,调用countDownLatch
countDownLatch.countDown();
}).start();
}
while (true) {
if (countDownLatch.getCount() == 0) {
System.out.println(num);
break;
}
}
}
}
- 打开cmd
# 编译
C:\work\git\spring_learn_repo\concurrent\src\main\java\com\xdclass\synopsis>javac -encoding UTF-8 UnsafeThread.java
# 反编译
C:\work\git\spring_learn_repo\concurrent\src\main\java\com\xdclass\synopsis>javap -c UnsafeThread.class
Compiled from "UnsafeThread.java"
public class com.xdclass.synopsis.UnSafeThread {
public com.xdclass.synopsis.UnSafeThread();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void inCreate();
Code:
0: getstatic #2 // Field num:I
3: iconst_1
4: iadd
5: putstatic #2 // Field num:I
8: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 29
8: new #3 // class java/lang/Thread
11: dup
12: invokedynamic #4, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
17: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
20: invokevirtual #6 // Method java/lang/Thread.start:()V
23: iinc 1, 1
26: goto 2
29: getstatic #7 // Field countDownLatch:Ljava/util/concurrent/CountDownLatch;
32: invokevirtual #8 // Method java/util/concurrent/CountDownLatch.getCount:()J
35: lconst_0
36: lcmp
37: ifne 29
40: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
43: getstatic #2 // Field num:I
46: invokevirtual #10 // Method java/io/PrintStream.println:(I)V
49: goto 52
52: return
static {};
Code:
0: iconst_0
1: putstatic #2 // Field num:I
4: new #18 // class java/util/concurrent/CountDownLatch
7: dup
8: bipush 10
10: invokespecial #19 // Method java/util/concurrent/CountDownLatch."<init>":(I)V
13: putstatic #7 // Field countDownLatch:Ljava/util/concurrent/CountDownLatch;
16: return
}
- 结论
0: getstatic #2 // Field num:I 获取指定类的静态域,并将其押入栈顶
3: iconst_1 将int型1押入栈顶
4: iadd 将栈顶两个int型
相加,将结果押入栈顶
5: putstatic #2 // Field num:I 为指定类静态域赋值
8: return
- 步骤
1、获取静态域num = 0
2、第1个线程对num加1
3、若这时创建第2个线程,对num加1
4、最后的预期结果应该为2,实际结果为1
# 产生线程不安全问题的原因:num++ 不是原子性操作,被拆分成好几个步骤,在多线程并发执行的情况下,因为cpu调度,多线程快递切换,有可能两个同一时刻都读取了同一个num值,之后对它进行+1操作,导致线程安全性
- 原子性操作
一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行
- 将非原子性操作变成原子性
使用synchronize关机字,使得操作具有原子性
volatile关键字仅仅保证可见性,并不保证原子性
- 代码案例
public class UnSafeThread {
private static int num = 0;
private static CountDownLatch countDownLatch = new CountDownLatch(10);
/**
* 每次调用对num进行++操作
*/
public static synchronized void inCreate() {
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 100; j++) {
inCreate();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//每个线程执行完成之后,调用countDownLatch
countDownLatch.countDown();
}).start();
}
while (true) {
if (countDownLatch.getCount() == 0) {
System.out.println(num);
break;
}
}
}
}
标签:java,public,num,static,countDownLatch,线程,安全性 来源: https://www.cnblogs.com/chniny/p/16258845.html