16. 单例模式
1. 饿汉式
-
代码
// 饿汉式: 可能会浪费资源 public class TestSingle01 { private TestSingle01(){ System.out.println(Thread.currentThread().getName() + " OK!"); } private final static TestSingle01 SINGLE = new TestSingle01(); public static TestSingle01 getInstance() { return SINGLE; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { TestSingle01.getInstance(); }).start(); } } }
2. 懒汉式
-
代码1
// 懒汉式: 多线程不安全 public class TestSingle02 { private TestSingle02(){ System.out.println(Thread.currentThread().getName() + " OK!"); } private static TestSingle02 SINGLE; public static TestSingle02 getInstance(){ if (SINGLE == null) { SINGLE = new TestSingle02(); } return SINGLE; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { TestSingle02.getInstance(); }).start(); } } }
-
加锁优化
// DCL懒汉式: 懒汉式(双重检测锁 + 加锁) // 不是原子性操作 public class TestSingle03 { private TestSingle03(){ System.out.println(Thread.currentThread().getName() + " OK!"); } private static TestSingle03 SINGLE; public static TestSingle03 getInstance(){ if (SINGLE == null) { synchronized (TestSingle03.class){ if (SINGLE == null) { /** * 不是原子性操作 * 1. 分配内存空间 * 2. 执行构造方法, 初始化对象 * 3. 把这个对象指向这个空间 */ SINGLE = new TestSingle03(); } } } return SINGLE; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { TestSingle03.getInstance(); }).start(); } } }
-
再次优化
public class TestSingle03 { private TestSingle03(){ System.out.println(Thread.currentThread().getName() + " OK!"); } // 加上volatile, 避免指令重排 private volatile static TestSingle03 SINGLE; public static TestSingle03 getInstance(){ if (SINGLE == null) { synchronized (TestSingle03.class){ if (SINGLE == null) { SINGLE = new TestSingle03(); } } } return SINGLE; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { TestSingle03.getInstance(); }).start(); } } }
3. 静态内部类
-
代码
// 静态内部类 public class TestSingle04 { private TestSingle04(){ System.out.println(Thread.currentThread().getName() + " OK!"); } public static TestSingle04 getInstance(){ return InnerClass.SINGLE; } public static class InnerClass{ private static final TestSingle04 SINGLE = new TestSingle04(); } public static void main(String[] args) { for (int i = 0; i < 1000; i++) { new Thread(() -> { TestSingle04.getInstance(); }).start(); } } }
4. 枚举类
-
代码
public class TestSingle05 { public static void main(String[] args) { EnumSingle instance1 = EnumSingle.INSTANCE; EnumSingle instance2 = EnumSingle.INSTANCE; System.out.println(instance1 == instance2); } } enum EnumSingle { INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } }
17. CAS
1. 说明
Compare and Swap
2. unsafe类
-
源码
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
-
说明:
比较当前工作内存中的值和主内存中的值, 如果这个值是期望的, 那么执行操作! 如果不是就一直循环
3.缺点
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- 可能导致ABA问题
4. ABA问题
-
代码
public class TestCAS01 { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020); // 如果期望的值达到了, 就更新; 否则, 就不更新 // 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, 6666)); System.out.println(atomicInteger.get()); } }
-
解决:
原子引用
public class TestCAS02 { public static void main(String[] args) { // 原子引用: 第一个参数是值, 第二个参数是版本号 AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(1, 1); new Thread(() -> { int stamp = reference.getStamp(); // 获取版本号 System.out.println("a1 => " + stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 如果值为1, 则改为5; 并把版本号+1 System.out.println(reference.compareAndSet(1, 5, reference.getStamp(), reference.getStamp() + 1)); System.out.println("a2 => " + reference.getStamp()); System.out.println(reference.compareAndSet(5, 1, reference.getStamp(), reference.getStamp() + 1)); System.out.println("a3 => " + reference.getStamp()); }, "a").start(); new Thread(() -> { int stamp = reference.getStamp(); // 获取版本号 System.out.println("b1 => " + stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(reference.compareAndSet(1, 6, reference.getStamp(), reference.getStamp() + 1)); System.out.println("b2 => " + reference.getStamp()); }, "b").start(); } }
18. 各种锁的理解
1. 公平锁, 非公平锁
-
公平锁: 公平, 不能插队. 必须先来后到
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
-
非公平锁: 不公平, 可以插队(默认)
public ReentrantLock() { sync = new NonfairSync(); }
2. 可重入锁
-
拿到外面的锁, 就可以自动获得你们的锁; 然后先释放里面的锁, 再释放外面的锁
-
代码
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"); } }
3. 自旋锁
- 代码
public class Demo02 { AtomicReference<Thread> atomicReference = new AtomicReference<Thread>(); // 加锁 public void myLock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + " ==> myLock" ); // 自旋锁 while (atomicReference.compareAndSet(null, thread)){ } } // 解锁 public void myUnLock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + " --> myUnLock" ); atomicReference.compareAndSet(thread, null); } public static void main(String[] args) { // ReentrantLock lock = new ReentrantLock(); // lock.lock(); // lock.unlock(); Demo02 lock = new Demo02(); new Thread(() -> { lock.myLock(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.myUnLock(); } }, "T1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { lock.myLock(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.myUnLock(); } }, "T2").start(); } }
4. 死锁
1. 死锁代码
-
代码
public class Demo03 { private static String lockA = "lockA"; private static String lockB = "lockB"; public static void main(String[] args) { new Thread(new MyThread(lockA, lockB), "A").start(); new Thread(new MyThread(lockB, lockA), "B").start(); } } class MyThread implements Runnable{ private String lockA; private String lockB; public MyThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "lock: " + lockA + " ==> get " + lockB); synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "lock: " + lockB + " ==> get " + lockA); } } } }
2. 排查
-
使用
jps -l
定位进程号 -
使用
jstack 进程号
找到死锁问题