定义:当多个线程访问某个类时,不管运行时环境采用 何种调度方式 或者这些线程将如何交替执行,并且在主调代码中 不需要任何额外的同步或协同 ,这个类都能表现出 正确的行为 ,那么就称这个类是线程安全的。
线程安全性主要体现在三个方面:原子性、可见性、有序性:
原子性在 JDK 中主要由两个方面体现出来:
一个是 JDK 中已经提供好的 Atomic 包,它们均使用了 CAS 完成线程的原子性操作(详见 【Java并发】浅析 AtomicLong & LongAdder )。
另一个是使用锁的机制来处理线程之间的原子性。锁主要包括:synchronized、lock。
依赖于 JVM 去实现锁,因此在这个关键字作用对象的作用范围内,都是同一时刻只能有一个线程对其进行操作的。synchronized 是 Java 中的一个关键字,是一种同步锁。它可以修饰的对象主要有四种:
注意:如果当前类是一个父类,子类调用父类的被 synchronized 修饰的方法,不会携带 synchronized 属性,因为 synchronized 不属于方法声明的一部分。
首先要说明的就是 Lock,通过查看 Lock 的源码可知,Lock 是一个接口。ReentrantLock 是唯一实现了 Lock 接口的类,意思是“可重入锁”,并且 ReentrantLock 提供了更多的方法。
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
锁的分类
可重入锁
synchronized
/ ReentrantLock
可中断锁
synchronized
不可中断, Lock
可中断 公平锁
synchronized
非公平锁 ReentrantLock
和 ReentrantReadWriteLock
默认情况下非公平锁,可设置为公平锁 读写锁
ReadWriteLock
/ ReentrantReadWriteLock
导致共享变量在线程间不可见的原因:
JVM 对于可见性,提供了 synchronized 和 volatile:
JMM 关于 synchronized 的两条规定:
volatile 的方式是:通过加入 内存屏障 和 禁止重排序 优化来实现。
Java 内存模型中,允许编译器和处理器对指令进行 重排序 ,但是重排序过程不会影响到 单线程 程序的执行,却会影响到多线程并发执行的正确性。而 Java 提供了 volatile、synchronized、Lock ,它们可以用来保证有序性。
另外,Java 内存模型具备一些先天的有序性,即不需要任何手段就能得到保证的有序性。通常被我们称为happens-before 原则(先行发生原则)。如果两个线程的执行顺序无法从 happens-before 原则推导出来,那么就不能保证它们的有序性,虚拟机就可以对它们进行重排序。
【以下规则摘抄自 《深入理解Java虚拟机》 】
笔记整理自: 【IMOOC】Java并发编程与高并发解决方案