前言:
本文内容:单例模式、深入理解CAS、原子引用解决ABA问题
推荐免费JUC并发编程视频:【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili
单例模式
饿汉式单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.jokerdig.single;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 Hungry () { } private final static Hungry HUNGRY = new Hungry (); public static Hungry getInstance () { return HUNGRY; } }
懒汉式单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.jokerdig.single;public class Lazy { private Lazy () { System.out.println(Thread.currentThread().getName()+"ok" ); } private volatile static Lazy lazy; public static Lazy getInstance () { if (lazy==null ){ synchronized (Lazy.class){ if (lazy==null ){ lazy = new Lazy (); } } } return lazy; } public static void main (String[] args) { for (int i = 0 ; i < 10 ; i++) { new Thread (()->{ lazy.getInstance(); }).start(); } } }
静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.jokerdig.single;public class Holder { private Holder () { } public static Holder getInstance () { return InnerClass.HOLDER; } public static class InnerClass { private static final Holder HOLDER = new Holder (); } }
反射破坏单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.jokerdig.single;import java.lang.reflect.Constructor;public class Reflex { private static boolean flag = false ; private Reflex () { synchronized (Reflex.class){ if (flag == false ){ flag=true ; }else { throw new RuntimeException ("不要试图破坏单例" ); } } System.out.println(Thread.currentThread().getName()+"ok" ); } private volatile static Reflex reflex; public static Reflex getInstance () { if (reflex ==null ){ synchronized (Lazy.class){ if (reflex==null ){ reflex = new Reflex (); } } } return reflex; } public static void main (String[] args) throws Exception{ Constructor<Reflex> constructor = Reflex.class.getDeclaredConstructor(null ); constructor.setAccessible(true ); Reflex instance1 = constructor.newInstance(); Reflex instance = constructor.newInstance(); System.out.println(instance); System.out.println(instance1); } }
枚举
通过源码得知,不能通过反射破坏枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.jokerdig.single;import java.lang.reflect.Constructor;public enum EnumSingle { INSTANCE; public EnumSingle getInstance () { return INSTANCE; } } class Test { public static void main (String[] args) throws Exception { EnumSingle instance1 = EnumSingle.INSTANCE; Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int .class); constructor.setAccessible(true ); EnumSingle instance2 = constructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
运行结果
1 2 3 4 5 6 7 Exception in thread "main" java.lang.NoSuchMethodException: com.jokerdig.single.EnumSingle.<init>() at java.lang.Class.getConstructor0(Class.java:3082 ) at java.lang.Class.getDeclaredConstructor(Class.java:2178 ) at com.jokerdig.single.Test.main(EnumSingle.java:26 ) Process finished with exit code 1
修改为有参构造后运行结果
1 2 3 4 5 6 Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417 ) at com.jokerdig.single.Test.main(EnumSingle.java:29 ) Process finished with exit code 1
枚举反编译源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 public abstract class Enum <E extends Enum <E>> implements Comparable <E>, Serializable { private final String name; public final String name () { return name; } private final int ordinal; public final int ordinal () { return ordinal; } protected Enum (String name, int ordinal) { this .name = name; this .ordinal = ordinal; } public String toString () { return name; } public final boolean equals (Object other) { return this ==other; } public final int hashCode () { return super .hashCode(); } protected final Object clone () throws CloneNotSupportedException { throw new CloneNotSupportedException (); } public final int compareTo (E o) { Enum<?> other = (Enum<?>)o; Enum<E> self = this ; if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass()) throw new ClassCastException (); return self.ordinal - other.ordinal; } @SuppressWarnings("unchecked") public final Class<E> getDeclaringClass () { Class<?> clazz = getClass(); Class<?> zuper = clazz.getSuperclass(); return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper; } public static <T extends Enum <T>> T valueOf (Class<T> enumType, String name) { T result = enumType.enumConstantDirectory().get(name); if (result != null ) return result; if (name == null ) throw new NullPointerException ("Name is null" ); throw new IllegalArgumentException ( "No enum constant " + enumType.getCanonicalName() + "." + name); } protected final void finalize () { } private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException ("can't deserialize enum" ); } private void readObjectNoData () throws ObjectStreamException { throw new InvalidObjectException ("can't deserialize enum" ); } }
深入理解CAS
什么是CAS
CAS(compare and swap),即比较交换 。CAS是一种基于锁的操作,而且是乐观锁。
Java中锁分为乐观锁和悲观锁
悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。
乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。
CAS 操作
简单练习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.jokerdig.cas;import java.util.concurrent.atomic.AtomicInteger;public class CASDemo { public static void main (String[] args) { AtomicInteger atomicInteger = new AtomicInteger (2022 ); atomicInteger.compareAndSet(2022 ,2023 ); System.out.println(atomicInteger.get()); atomicInteger.compareAndSet(2022 ,2021 ); System.out.println(atomicInteger.get()); } }
Unsafe类
CAS缺点:
循环会耗时
一次性只能保证一个共享变量的原子性
ABA问题
原子引用解决ABA问题
ABA问题
ABA 问题是指在CAS操作时,其他线程将变量值A改为了B,但是又被改回了A,等到本线程使用期望值A与当前变量进行比较时,发现变量A没有变,于是CAS就将A值进行了交换操作,但是实际上该值已经被其他线程改变过,这与乐观锁的设计思想不符合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.jokerdig.cas;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.atomic.AtomicStampedReference;public class ABA { public static void main (String[] args) { AtomicInteger atomicInteger = new AtomicInteger (23 ); atomicInteger.compareAndSet(23 ,22 ); System.out.println(atomicInteger.get()); atomicInteger.compareAndSet(22 ,23 ); System.out.println(atomicInteger.get()); atomicInteger.compareAndSet(23 ,66 ); System.out.println(atomicInteger.get()); } }
原子引用解决ABA
带版本号的原子操作,原理:使用乐观锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.jokerdig.cas;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.atomic.AtomicStampedReference;public class ABA { public static void main (String[] args) { AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference <>(23 ,1 ); new Thread (()->{ int stamp = atomicStampedReference.getStamp(); System.out.println("A1=>" +stamp); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(23 , 22 , atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 ); System.out.println("A2=>" +atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(22 , 23 , atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 )); System.out.println("A3=>" +atomicStampedReference.getStamp()); },"A" ).start(); new Thread (()->{ int stamp = atomicStampedReference.getStamp(); System.out.println("B1=>" +stamp); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(23 , 66 , stamp, stamp + 1 )); System.out.println("B2=>" +atomicStampedReference.getStamp()); },"B" ).start(); } }
运行结果
1 2 3 4 5 6 7 8 9 10 11 A1=>1 B1=>1 true B2=>2 A2=>2 false A3=>2 Process finished with exit code 0