什么是线程安全性
《Java Concurrency In Practice》对线程安全的定义如下:
当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方代码不作其他的协调,这个类的行为仍是正确的,那么称这个类是线程安全的。
简单理解就是,多线程环境下访问这个对象,我们不必考虑对象里面方法间协调的问题,对象的行为依然正确。通常这些类自己已经做好了同步等工作,比如,并发访问对象的共享变量时加上锁等同步机制。
多线程环境下,如果没有正确的同步机制,非常容易出错,比如非原子性操作导致的并发操作结果错误、线程运行顺序导致的程序错误、线程活跃性,如死锁、活锁、线程饥饿等都会导致程序结果未达预期。下面简单介绍下各种情况导致的错误及解决方案。
原子性
非原子操作在多线程程序中出错经典的例子,如,i++操作。看似一个操作,实际分成了三步,先从内存中读取,然后增加,最后写回内存。下面看个例子:
/**
* @author kangming.ning
* @date 2023-02-16
* @since 1.0
**/
public class SelfAddTest {
static int i;
public static void main(String[] args) throws InterruptedException {
Runnable runnable=()->{
selfAdd();
};
Thread thread1=new Thread(runnable);
thread1.start();
Thread thread2=new Thread(runnable);
thread2.start();
thread1.join();
thread2.join();
System.out.println(i);
}
private static void selfAdd(){
for (int j=0;j<10000;j++){
i++;
}
}
}
结果打印不定,通常小于预期值2000。这说明程序出错了。下面我们反编译class文件,看生成的汇编代码,看看i++到底生成了哪些指令(javap )
输入指令:javap -verbose -c -l -private .\SelfAddTest.class
Last modified 2023-2-17; size 1614 bytes
MD5 checksum 96b2b83020dc44d8755a9c241ba70d76
Compiled from "SelfAddTest.java"
public class com.kmning.wallet.myconcurrent.SelfAddTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #12.#39 // java/lang/Object."<init>":()V
#2 = InvokeDynamic #0:#44 // #0:run:()Ljava/lang/Runnable;
#3 = Class #45 // java/lang/Thread
#4 = Methodref #3.#46 // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
#5 = Methodref #3.#47 // java/lang/Thread.start:()V
#6 = Methodref #3.#48 // java/lang/Thread.join:()V
#7 = Fieldref #49.#50 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Fieldref #11.#51 // com/kmning/wallet/myconcurrent/SelfAddTest.i:I
#9 = Methodref #52.#53 // java/io/PrintStream.println:(I)V
#10 = Methodref #11.#54 // com/kmning/wallet/myconcurrent/SelfAddTest.selfAdd:()V
#11 = Class #55 // com/kmning/wallet/myconcurrent/SelfAddTest
#12 = Class #56 // java/lang/Object
#13 = Utf8 i
#14 = Utf8 I
#15 = Utf8 <init>
#16 = Utf8 ()V
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 LocalVariableTable
#20 = Utf8 this
#21 = Utf8 Lcom/kmning/wallet/myconcurrent/SelfAddTest;
#22 = Utf8 main
#23 = Utf8 ([Ljava/lang/String;)V
#24 = Utf8 args
#25 = Utf8 [Ljava/lang/String;
#26 = Utf8 runnable
#27 = Utf8 Ljava/lang/Runnable;
#28 = Utf8 thread1
#29 &#
标签:lang,Java,Thread,Utf8,.#,读懂,线程,java
From: https://blog.csdn.net/u012882823/article/details/139865328