synchronized能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
synchronized (锁对象) {
// 受保护资源
}
一、synchronized保证原子性
案例演示:5个线程各执行1000次 i++;
对number++;增加同步代码块后,保证同一时间只有一个线程操作number++;。就不会出现安全问题。
总结:
synchronized保证原子性的原理,synchronized保证只有一个线程拿到锁,能够进入同步代码块。
二、synchronized保证可见性案例演示:一个线程根据boolean类型的标记flag, while循环,另一个线程改变这个flag变量的值,另一个线程并不会停止循环。
synchronized保证可见性的原理,执行synchronized时,会对应lock原子操作会刷新工作内存中共享变量的值
三、synchronized保证有序性 3.1 为什么要重排序为了提高程序的执行效率,编译器和CPU会对程序中代码进行重排序。
3.2 as-if-serial语义as-if-serial语义的意思是:不管编译器和CPU如何重排序,必须保证在单线程情况下程序的结果是正确的。
以下数据有依赖关系,不能重排序。
写后读:
int a = 1;
int b = a;
写后写:
int a = 1;
int a = 2;
读后写:
int a = 1;
int b = a;
int a = 2;
编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序。
int a = 1;
int b = 2;
int c = a + b;
上面3个操作的数据依赖关系如图所示:
如上图所示a和c之间存在数据依赖关系,同时b和c之间也存在数据依赖关系。因此在最终执行的指令序列中,c不能被重排序到a和b的前面。
但a和b之间没有数据依赖关系,编译器和处理器可以重排序a和b之间的执行顺序。
下图是该程序的两种执行顺序。
可以这样:
int a = 1;
int b = 2;
int c = a + b;
也可以重排序这样:
int b = 2;
int a = 1;
int c = a + b;
3.3 使用synchronized保证有序性
总结:
synchronized后,虽然进行了重排序,保证只有一个线程会进入同步代码块,也能保证有序性。
synchronized保证有序性的原理,我们加synchronized后,依然会发生重排序,只不过,我们有同步代码块,可以保证只有一个线程执行同步代码中的代码。保证有序性
视频教程