暮年|20年架构师深入讲解java多线程与高并发:volatile与CAS,涨薪5K( 五 )

下面有一个小概念 , 你在某一种特定的不小心的情况下你把o变成了别的对象了 , 这个时候线程的并发就会出问题 。 锁是在对象的头上两位来作为代表的 , 你这线程本来大家都去访问这两位了 , 结果突然把这把锁变成别的对象 , 去访问别的对象的两位了 , 这俩之间就没有任何关系了 。 因此 , 以对象作为锁的时候不让它发生改变 , 加fifinal 。
/*** 锁定某对象o , 如果o的属性发生改变 , 不影响锁的使用* 但是如果o变成另外一个对象 , 则锁定的对象发生改变* 应该是避免将锁定对象的引用变成另外的对象* @author mashibing*/package com.mashibing.juc.c_017;import java.util.concurrent.TimeUnit;public class T{final Object o = new Object();void m(){synchronized(o){while(true){try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){e.printStace();}System.out.println(Thread.currentThread().getName());}}}public static void main(String[] args){T t =new T();//启动第一个线程new Thread(t::m,"t1").start();好 , 那么到现在为止 , 我们volatile和synchronized都已经基本讲完了 , 稍微简单的回顾一下 。
synchronized锁的是对象而不得代码 , 锁方法锁的是this , 锁static方法锁的是class , 锁定方法和非锁定方法是可以同时执行的 , 锁升级从偏向锁到自旋锁到重量级锁
volatile 保证线程的可见性 , 同时防止指令重排序 。 线程可见性在CPU的级别是用缓存一直性来保证的;禁止指令重排序CPU级别是你禁止不了的 , 那是人家内部运行的过程 , 提高效率的 。 但是在虚拟机级别你家volatile之后呢 , 这个指令重排序就可以禁止 。 严格来讲 , 还要去深究它的内部的话 , 它是加了读屏障和写屏障 , 这个是CPU的一个原语 。
注:关于synchronized和volatile的底层实现 , 在老师的JVM课程中会有深入到CPU级别的讲解
CAScas号称是无锁优化 , 或者叫自旋 。 这个名字无所谓 , 理解它是干什么的就行 , 概念这个东西是人为了描述问题解决问题而定义出来的 , 所以怎么定义不是很重要 , 重点是在解决问题上我们通过Atomic类(原子的) 。 由于某一些特别常见的操作 , 老是来回的加锁 , 加锁的情况特别多 , 所以干脆java就提供了这些常见的操作这么一些个类 , 这些类的内部就自动带了锁 , 当然这些锁的实现并不是synchronized重量级锁 , 而是CAS的操作来实现的(号称无锁) 。
我们来举例几个简单的例子 , 凡是以Atomic开头的都是用CAS这种操作来保证线程安全的这么一些个类 。 AtomicInteger的意思就是里面包了一个Int类型 , 这个int类型的自增 count++ 是线程安全的 , 还有拿值等等是线程安全的 , 由于我们在工作开发中经常性的有那种需求 , 一个值所有的线程共同访问它往上递增, 所以jdk专门提供了这样的一些类 。 使用方法AtomicInteger如下代码
try{TimeUnit.SECONDS.sleep(3);}catch(InterruptedException e){e.printStackTrace();}//创建第二个线程Thread t2 =new Thread(t::m,"t2");t.o=new Object();//锁对象发生改变 , 所以t2线程得以执行 , 如果注释掉这句话 , 线程2将永远得不到执行机会t2.start();}}/*** 解决同样的问题的高效方法 , 使用AtomXXX类* AtomXXX类的本身方法都是原子性的 , 但不能保证多个方法连续调用都是原子性的* @author mashibing*/package com.mashibing.juc.c_018_00_AtomicXXX;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;public class T01_AtomicInteger {/*volatile*/ //int count1 = 0;AtomicInteger count = new AtomicInteger(0);/*synchronized*/ void m() {for (int i = 0; i