4.2.2 锁
synchronized:依赖 JVM
- 修饰代码块:大括号括起来的代码,作用于调用的对象
- 修饰方法: 整个方法,作用于调用的对象
- 修饰静态方法:整个静态方法,作用于所有对象
package com.mmall.concurrency.example.count; import com.mmall.concurrency.annoations.ThreadSafe; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * @author JavaEdge */ @Slf4j @ThreadSafe public class CountExample3 { /** * 请求总数 */ public static int clientTotal = 5000; /** * 同时并发执行的线程数 */ public static int threadTotal = 200; public static int count = 0; public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal ; i++) { executorService.execute(() -> { try { semaphore.acquire(); add(); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("count:{}", count); } private synchronized static void add() { count++; } }
synchronized 修正计数类方法
- 修饰类:括号括起来的部分,作用于所有对象
子类继承父类的被 synchronized 修饰方法时,是没有 synchronized 修饰的!!!
Lock: 依赖特殊的 CPU 指令,代码实现
4.2.3 对比
- synchronized: 不可中断锁,适合竞争不激烈,可读性好
- Lock: 可中断锁,多样化同步,竞争激烈时能维持常态
- Atomic: 竞争激烈时能维持常态,比Lock性能好; 只能同步一
个值
4.3 可见性
你做的改变,别人看不见。
一个线程对主内存的修改可以及时的被其他线程观察到
4.3.1 导致共享变量在线程间不可见的原因
- 线程交叉执行
- 重排序结合线程交叉执行
- 共享变量更新后的值没有在工作内存与主存间及时更新
4.3.2 可见性之synchronized
JMM关于synchronized的规定
- 线程解锁前,必须把共享变量的最新值刷新到主内存
- 线程加锁时,将清空工作内存中共享变量的值,从而使
用共享变量时需要从主内存中重新读取最新的值(加锁与解锁是同一把锁)
4.3.3 可见性之volatile
通过加入内存屏障和禁止重排序优化来实现
- 对volatile变量写操作时,会在写操作后加入一条store
屏障指令,将本地内存中的共享变量值刷新到主内存
- 对volatile变量读操作时,会在读操作前加入一条load
屏障指令,从主内存中读取共享变量
- volatile使用
volatile boolean inited = false; //线程1: context = loadContext(); inited= true; // 线程2: while( !inited ){ sleep(); } doSomethingWithConfig(context)
4.4 有序性
不按套路出牌。
一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序
JMM允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性
4.4.1 happens-before 规则
5发布对象
- 发布对象
使一个对象能够被当前范围之外的代码所使用 - 对象逸出
一种错误的发布。当-个对象还没有构造完成时,就使它被其他线程所见
5.1 安全发布对象
- 在静态初始化函数中初始化一个对象引用
- 将对象的引用保存到volatile类型域或者AtomicReference对象中
- 将对象的引用保存到某个正确构造对象的final类型域中
- 将对象的引用保存到一个由锁保护的域中
package com.mmall.concurrency.example.singleton; import com.mmall.concurrency.annoations.NotThreadSafe; /** * 懒汉模式 -》 双重同步锁单例模式 * 单例实例在第一次使用时进行创建 * @author JavaEdge */ @NotThreadSafe public class SingletonExample4 { /** * 私有构造函数 */ private SingletonExample4() { } // 1、memory = allocate() 分配对象的内存空间 // 2、ctorInstance() 初始化对象 // 3、instance = memory 设置instance指向刚分配的内存 // JVM和cpu优化,发生了指令重排 // 1、memory = allocate() 分配对象的内存空间 // 3、instance = memory 设置instance指向刚分配的内存 // 2、ctorInstance() 初始化对象 /** * 单例对象 */ private static SingletonExample4 instance = null; /** * 静态的工厂方法 * * @return */ public static SingletonExample4 getInstance() { // 双重检测机制 // B if (instance == null) { // 同步锁 synchronized (SingletonExample4.class) { if (instance == null) { // A - 3 instance = new SingletonExample4(); } } } return instance; } }












