请你谈谈对Volatile的理解
Volatile是Java虚拟机提供轻量级的同步机制。
什么是JMM
JVM:Java内存模型,不存在的东西,他是一个概念、约定。
关于JMM的一些同步的约定:
立刻
刷回主存。线程:工作内存、主内存
八种操作:
内存交互操作
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
JMM对这八种指令的使用,制定了如下规则:
实践
public class Demo {
private static int num = 0;
public static void main(String[] args) throws InterruptedException { // main
new Thread(()->{ // 线程对主内存的变化不知道
while (num ==0){
}
}).start();
TimeUnit.SECONDS.sleep(1);
num = 1;
System.out.println(num);
}
}
问题:程序不知道主内存的值被修改过了。
保证可见性
增加volatile增加可见性
private volatile static int num = 0;
不保证原子性
原子性:不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
public class Demo02 {
private volatile static int num = 0;
public static void add(){
num ++ ;
}
public static void main(String[] args) {
// 理论上num结果应该为20000
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while(Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "" +num);
}
}
如果不加 lock 和 synchronized,怎么样保证原子性。
使用原子类Atomic,解决原子性问题
public class Demo02 {
private volatile static AtomicInteger num = new AtomicInteger();
public static void add(){
//num ++ ; // 不是一个原子性操作
num.getAndIncrement(); // AtomicInteger + 1 ,CAS
}
public static void main(String[] args) {
// 理论上num结果应该为20000
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while(Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "" +num);
}
}
什么是指令重排
你写的程序,计算机并不是按照你写的那样去执行的。
源代码 --> 编译器优化重排–>指令并行也可能会重排–>内存系统也会重排-> 执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性!
Volatile可以避免指令重排:内存屏障。CPU指令:
饿汉式
public class Hungry {
// 可能浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
private static Hungry getInstance(){
return HUNGRY;
}
}
DCL懒汉式
// 懒汉式
public class LazyMan {
private static boolean mango = false;
private LazyMan(){
synchronized (LazyMan.class){
if(mango == false){
mango = true;
}else{
throw new RuntimeException("不要试图使用破坏");
}
if(LazyMan!=null){
throw new RuntimeException("不要试图使用破坏");
}
}
}
private volatile static LazyMan LazyMan;
public static LazyMan getInstance(){
// 双重检测锁模式,懒汉式单例,DCL懒汉式加锁
if(LazyMan == null){
synchronized (LazyMan.class){
if(LazyMan == null){
LazyMan = new LazyMan(); // 不是原子性操作
}
}
}
return LazyMan;
}
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*/
// 多线程
// public static void main(String[] args) {
// for (int i = 0; i < 10; i++) {
// new Thread(()->{
// LazyMan.getInstance();
// }).start();
// }
// }
// 反射
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
// LazyMan instance = LazyMan.getInstance();
Field mango = LazyMan.class.getDeclaredField("mango");
mango.setAccessible(true);
Constructor<LazyMan> declaredConstructors = LazyMan.class.getDeclaredConstructor();
declaredConstructors.setAccessible(true);
LazyMan instance2 = declaredConstructors.newInstance();
mango.set(instance2,false);
LazyMan instance1 = declaredConstructors.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
静态内部类
public class Holder {
private Holder(){
}
public static class InnerClass{
private static final Holder HOLDER= new Holder();
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
}
单利不安全
使用枚举
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance3 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
}
}
什么是CAS
Unsafe
public class CASDemo {
// CAS compareAndSet:比较并交换!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果我期望的值达到了,那么久更新,否则,就不更新 CAS 是CPU的并发愿语!
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
CAS缺点:
CAS:ABA问题(狸猫换太子)
public class CASDemo {
// CAS compareAndSet:比较并交换!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 对于我们平时写的SQL:乐观锁!
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果我期望的值达到了,那么久更新,否则,就不更新 CAS 是CPU的并发愿语!
// =======捣乱的线程========
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
// =======期望的线程========
System.out.println(atomicInteger.compareAndSet(2020, 213123));
System.out.println(atomicInteger.get());
}
}
解决ABA问题,引入原子引用!
public class CASDemo {
// CAS compareAndSet:比较并交换!
public static void main(String[] args) {
// AtomicInteger atomicInteger = new AtomicInteger(2020);
AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);
// 对于我们平时写的SQL:乐观锁!
new Thread(
()->{
int stamp = atomicInteger.getStamp(); // 获得版本号
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a2=>"+atomicInteger.getStamp());
System.out.println(atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a3=>"+atomicInteger.getStamp());
},"A"
).start();
new Thread(
()->{
int stamp = atomicInteger.getStamp(); // 获得版本号
System.out.println("b=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(1, 4, stamp, stamp + 1));
System.out.println("b2=>"+atomicInteger.getStamp());
},"B"
).start();
}
}
非公平锁:非常不公平,可以插锁(默认非公平)
public ReentrantLock() {
sync = new NonfairSync();
}
公平锁:非常公平,不能够插队,必须先来后到!
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
Synchronized
public class demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"sms");
call(); // 这列也有锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"call");
}
}
lock
public class Demo02 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
Lock lock = new ReentrantLock();
public void sms(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"sms");
call(); // 这列也有锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class spinLockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
// 加锁
public void MyLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "==> mylock");
// 自旋锁
while (atomicReference.compareAndSet(null,thread)){
}
}
// 解锁
public void MyUnLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "==> mylock");
// 自旋锁
atomicReference.compareAndSet(thread,null);
}
}
使用自己封装的自旋锁:
public class TestSpinLock {
public static void main(String[] args) throws InterruptedException {
spinLockDemo lock = new spinLockDemo();
new Thread(()->{
lock.MyLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.MyUnLock();
}
},"T1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
lock.MyLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.MyUnLock();
}
},"T2").start();
}
}
什么是死锁
public class DeadLock {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String LockA;
private String LockB;
public MyThread(String lockA, String lockB) {
LockA = lockA;
LockB = lockB;
}
@SneakyThrows
@Override
public void run() {
synchronized (LockA){
System.out.println(Thread.currentThread().getName() + "lock:" + LockA +"=>get"+LockB);
TimeUnit.SECONDS.sleep(2);
synchronized (LockB){
System.out.println(Thread.currentThread().getName() + "lock:" + LockB+"=>get"+LockA);
}
}
}
}
解决问题
jstack 进程号
找到死锁问题