synchronized(锁对象) { // 受保护资源; }
目标:学习使用 synchronized 保证原子性的原理
使用 synchronized 保证原子性
案例:5个线程各执行10000次 i++
package juc.synchronized_test.prove; ? import java.util.ArrayList; import java.util.List; ? /** * @author : 雪飞oubai * @date : 2020/4/9 11:27 * 目标:演示原子性问题 * 1、定义一个共享变量 number * 2、对number进行1000次 ++ 操作 * 3、使用 5 个线程来进行 */ public class Test02Atomicity { //1、定义一个共享变量 number private static int number = 0; private static Object obj = new Object(); ? public static void main(String[] args) throws InterruptedException { Runnable increment = () -> { for (int i = 0; i < 10000; i++) { synchronized (obj) { number++; } } }; List<Thread> list = new ArrayList<>(); ? for (int i = 0; i < 5; i++) { Thread t = new Thread(increment); t.start(); list.add(t); } for (Thread thread : list) { thread.join(); } System.out.println("number=" + number); } }
synchronized 保证原子性的原理
对 number++;增加同步代码快,保证同一时间只有一个线程操作 number++;。就不会出现安全问题;
synchronized 与可见性
目标:学习使用 synchronized 保证可见性的原理
使用 synchronized 保证可见性
案例演示:一个线程根据 boolean 类型标记 flag,while循环,另一个线程改变这个flag变量的值,另一个线程不会停止循环
package juc.synchronized_test.prove; ? import java.util.concurrent.TimeUnit; ? /** * @author : 雪飞oubai * @date : 2020/4/9 11:07 * 目的:演示可见性问题 * 1、创建一个贡献变量 * 2、创建一条线程不断的读取共享变量 * 3、创建一条线程修改共享变量 */ public class Test01Visibility { // 多个线程都会访问的数据,我们称为线程的共享数据 private static boolean flag = true; private static Object obj = new Object(); ? public static void main(String[] args) throws InterruptedException { new Thread(() -> { while (flag) { synchronized (obj) { } } }).start(); ? TimeUnit.SECONDS.sleep(1); ? new Thread(() -> { flag = false; System.out.println("时间到,线程2设置为false"); }).start(); } }
小结:
synchronized 保证可见性的原理,执行 synchronized时,会对应执行 lock 原子操作会刷新工作内存中共享变量的值;
synchronized 与有序性
目标:学习使用 synchronized 保证有序性的原理
为什么要重排序
为了提高程序的执行效率,编译器和CPU对象程序中的代码进行重排序;
as-if-serial语义
as-if-serial语义的意思是:不管编译器和CPU如何重排序,必须保证单线程情况下程序的结果是正确的;
以下数据有依赖关系,不能重排序
写后读
int a = 1; int b = a;
写后写
int a = 1; int b = a;
读后写
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 之间的执行顺序;下图是该程序的两种执行顺序;
原文:https://www.cnblogs.com/lililixuefei/p/13186068.html