简单来说,在多个线程访问某个方法或者对象的时候,不管通过任何的方式调用以及线程如何去交替执行。
在程序中不做任何同步干预操作的情况下 ,这个方法或者对象的执行/修改都能按照预期的结果来反馈 ,那么这个类就是线程安全的。
实际上 ,线程安全问题的具体表现体现在三个方面,原子性、有序性、可见性。
原子性呢,是指当一个线程执行一系列程序指令操作的时候,它应该是不可中断的,因 为一旦出现中断,站在多线程的视角来看,这一系列的程序指令会出现前后执行结果不一致的问题。这个和数据库里面的原子性是一样的,简单来说就是一段程序只能由一个线程完整的执行完成 ,而不能存在多个线程干扰。(如图)CPU 的上下文切换 ,是导致原子性问题的核心 ,而 JVM 里面提供了Synchronized 关键字来解决原子性问题。
可见性,就是说在多线程环境下 ,由于读和写是发生在不同的线程里面,有可能出现某个线程对共享变量的修改 ,对其他线程不是实时可见的。
导致可见性问题的原因有很多 ,比如 CPU 的高速缓存、CPU 的指令重排序、编译器的指令重排序。有序性 ,指的是程序编写的指令顺序和最终 CPU 运行的指令顺序可能出现不一致的现象 ,这种现象也可以称为指令重排序 ,所以有序性也会导致可见性问题。
可见性和有序性可以通过 JVM 里面提供了一个 Volatile 关键字来解决。
在我看来,导致有序性、原子性、可见性问题的本质,是计算机工程师为了最大化提升 CPU 利用率导致的。比如为了提升 CPU 利用率,设计了三级缓存、设计了 StoreBuffer、 设计了缓存行这种预读机制、在操作系统里面,设计了线程模型、在编译器里面,设计了编译器的深度优化机制。