JUC学习笔记——04.单例,CAS和各种锁

16. 单例模式

1. 饿汉式
  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();
            }
        }
    }
    

    image-20201214170200002

2. 懒汉式
  1. 代码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();
            }
        }
    }
    

    image-20201214170117491

  2. 加锁优化

    // 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();
            }
        }
    }
    

    image-20201214170440122

  3. 再次优化

    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. 静态内部类
  1. 代码

    // 静态内部类
    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. 枚举类
  1. 代码

    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类
  1. 源码

    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;
    }
    

    image-20201214205721276

  2. 说明:

    比较当前工作内存中的值和主内存中的值, 如果这个值是期望的, 那么执行操作! 如果不是就一直循环

3.缺点
  1. 循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. 可能导致ABA问题
4. ABA问题
  1. 代码

    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());
        }
    }
    

    image-20201214213132729

  2. 解决:

    原子引用

    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. 公平锁, 非公平锁
  1. 公平锁: 公平, 不能插队. 必须先来后到

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    
  2. 非公平锁: 不公平, 可以插队(默认)

    public ReentrantLock() {
        sync = new NonfairSync();
    }
    
2. 可重入锁
  1. 拿到外面的锁, 就可以自动获得你们的锁; 然后先释放里面的锁, 再释放外面的锁

    image-20201214220603239

  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");
        }
    }
    

    image-20201214221450459

3. 自旋锁
  1. 代码
    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();
        }
    }
    
    image-20201214223624066
4. 死锁
1. 死锁代码
  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);
                }
            }
        }
    }
    

    image-20201214224827399

2. 排查
  1. 使用jps -l定位进程号 image-20201214225307132

  2. 使用jstack 进程号找到死锁问题 ​ image-20201214225530210

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值