JUC 第一天:lock锁与多线程
    1. 是什么?
    java.util.concurrent 包的简称! 这里面有很多并发编程时需要使用的工具类!

    1. 2. 多线程相关内容回顾:面试题! *****
    2. 线程:
    3. 通常一个进程中包含若干个线程, 分配资源的基本单位!
    4. 进程:
    5. 具有一定独立功能的程序!
    6. 并发:
    7. 在同一时刻访问同一个资源。
    8. 并行:
    9. 多个工作一起执行,之后汇总到一起。是一个整体!
    10. 打扫卫生!
    11. wait
    12. 释放资源锁!
    13. Object 类!
    14. sleep
    15. 不释放资源锁!
    16. Thread 类!
    17. 3. 如何创建线程:
    18. 1. 继承:
    19. Thread
    20. 2. 实现:
    21. Runnable
    22. Callable 接口!后续处理!
    23. // 匿名内部类创建线程!
    24. new Thread(new Runnable() {
    25. @Override
    26. public void run() {
    27. System.out.println("我是匿名的....");
    28. }
    29. }).start();
    30. // 学过拉姆达表达式!
    31. // 回顾拉姆达表达式
    32. ()->{}
    33. 箭头右边:方法体!
    34. 箭头左边:方法的参数!
    35. public abstract void run();
    36. new Thread(()->{System.out.println("我是拉姆达表达式");}).start();
    37. // 公式:复制小括号,写死右箭头,落地花括号!
    38. 案例:
    39. 当接口中只有一个抽象方法时,这样的接口叫函数式接口! 才可以使用拉姆达表达式去写!
    40. @FunctionalInterface 这个注解的作用就是: 如果这个接口中出现了多个抽象方法,会给你提示错误信息!
    41. interface Foo {
    42. public int add(int x, int y);
    43. }
    44. 函数式接口中是否可以存在其他方法?
    45. @FunctionalInterface
    46. interface Foo{
    47. // 定义一个抽象方法!
    48. int add(int x,int y);
    49. // 除法!
    50. // int div(int x,int y);
    51. // 这个是默认方法:
    52. default int div(int x,int y){
    53. return x/y;
    54. }
    55. // 还可以有静态方法:
    56. // static 特性: a. 随着类的加载而加载 b. 优先于对象的存在 c. 类名.方法();
    57. static int decr(int x,int y){
    58. return x-y;
    59. }
    60. }
    61. 必会:*****
    62. 当接口中只有一个抽象方法时,这样的接口叫函数式接口! 才可以使用拉姆达表达式去写!
    63. @FunctionalInterface 这个注解的作用就是: 如果这个接口中出现了多个抽象方法,会给你提示错误信息!
    64. 公式:复制小括号,写死右箭头,落地花括号!
    65. 4. sync 回顾:
    66. 这个关键字什么时候会用?
    67. 多线程出现资源抢占的时候!
    68. 回顾卖票的案例!
    69. 多线程编程模板上: 线程操作资源类!
    70. 重点: synchronized 八种锁问题!
    71. 总结:*****
    72. 1. 两个同步方法执行的时候,这个两个方法的使用的应该是同一把锁! 这个锁是谁呢? 当前对象this
    73. 2. 如果是一个同步方法,一个是普通方法。则同步方法有锁,普通方法没有锁!按照顺序执行!
    74. 3. 如果是两部手机调用的同步方法,他们不是同一把锁!按照顺序执行!
    75. Phone phone = new Phone();
    76. Phone phone2 = new Phone();
    77. 4. 静态同步方法:锁字节码文件!普通同步方法:锁 this
    78. 对于同步方法块,锁是Synchonized括号里配置的对象!
    79. 5. Lock 接口!
    80. lock() 上锁!
    81. tryLock()
    82. tryLock(long time, TimeUnit unit)
    83. newCondition() 获取钥匙
    84. unlock() 解锁!
    85. a. ReentrantLock 先看构造:
    86. ReentrantLock(); 非公平锁!
    87. ReentrantLock(boolean fair); false 非公平锁! true 公平锁!
    88. b. 使用!*****
    89. 可以代替sync
    90. lock.lock();
    91. try{
    92. // 业务逻辑.....
    93. }finally{
    94. lock.unlock();
    95. }
    96. 5.1 可重入锁: *****
    97. 同一个线程,在外层获取到锁的时候,在进入线程的内部同步方法时,会自动获取到锁!这就叫可重入锁!
    98. sync ReentrantLock 都属于可重入锁!
    99. 在一定程度上可以避免死锁的产生!
    100. 5.2 测试公平锁 限时等待:
    101. private Lock lock = new ReentrantLock(true);
    102. 注意点:
    103. 有加锁,就必须有解锁!
    104. 5.3 ReentrantLocksynchronized区别! *****
    105. a. synchronized and ReentrantLock 独占锁,可重入! 加锁,解锁不受程序员控制器,都是有JVM 操作!ReentrantLock 反之!操作灵活!
    106. b. synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断!
    107. 5.4 ReentrantReadWriteLock 读写锁!
    108. 构造:
    109. ReentrantReadWriteLock()
    110. ReentrantReadWriteLock(boolean fair)
    111. 读写锁:
    112. 允许同一时刻被多个读线程访问数据,但是,在写线程中,只有允许一个线程!其他都会阻塞!
    113. 案例:
    114. 读方法,写方法!
    115. 有问题案例:
    116. 优化案例:
    117. 特点: *****
    118. 写写,读写,写读 不能并发操作!
    119. 读读 可并发!
    120. 锁的降级:
    121. 从写锁变为读锁 就是锁的降级!释放写锁,再释放读锁!
    122. 读写锁总结 *****
    123. 优点:
    124. 支持,公平、非公平锁!
    125. 可重入!
    126. 锁的降级,不支持锁升级!
    127. 缺点:
    128. 容易产生饥饿问题!
    129. 读多,写少的情况下! 可以使用公平模式解决!以牺牲系统的吞吐量为代价!
    130. 写锁可以获取到Condition 对象,但是,读锁不可以获取,会抛出异常!
    131. 6. 线程间的通信:
    132. 多线程编程模板上:线程操作资源类!
    133. 多线程编程模板中:判断,干活,通知!
    134. 两个线程操作一个初始值为0的变量,实现一个线程对变量增加1,一个线程对变量减少1,交替10轮!
    135. 两个是没有问题的! 都是1,0
    136. 如果出现一对以上的线程就不是1,0了!
    137. 原因:*****
    138. 虚假唤醒!
    139. if---->while
    140. 多线程编程模板下: 防止虚假唤醒 判断使用while
    141. 线程间通信:对标! *****
    142. sync ---> lock
    143. Object Condition
    144. wait(): await();
    145. notify(): signal();
    146. notifyAll(): signalAll();

    JUC 第二天:
    回顾:
    1. 回顾有关于多线程的面试题:
    线程,进程:
    并发,并行:
    wait与sleep:

            2.    有关于锁 sync 回顾!
                    sync ----> lock
                    Object             Condition
                    wait():          await();
                    notify():        signal();
                    notifyAll():    signalAll();
    
    
            3.    LOCK 接口:
                    ReentrantLock和synchronized区别!
    
                    lock.lokc();
                    try{
                        //    业务逻辑!
                    }finally{
                        lock.unlock();
                    }
    
                    限时等待方法:lock.tryLock(param1,param2);
                    可重入锁,独占锁
                    公平与非公平锁!
                    sync a(){
                        b();
                    }
                    sync b(){};
    
                    读写锁:
                        特点:
                            读读可并发,读写,写读,写写都不可以并发!
    
                        从写锁到读锁这个过程,解锁写锁读锁,这个过程称之为锁的降级!
    
                        读写锁最大的缺点:很容易会产生'饥饿'状态!
                        在读线程多,写线程少的情况下! 使用公平与非公平机制来调节,以牺牲系统的吞吐量为代价!
    
    
            4.    线程间通信:
                        Object             Condition
                        wait():          await();
                        notify():        signal();
                        notifyAll():    signalAll();
    
                    多线程编程模板:
                        上:线程操作资源类,
                        中:判断干活通知,
                        下:防止虚假唤醒! 在使用多线程判断的时候 if ---> while 
    
    1.    线程间定制化通信!
            AA打印5次,BB打印10次,CC打印15次
            接着
            AA打印5次,BB打印10次,CC打印15次
            。。。打印10轮
    
            分析:
                1.    Lock 有锁!
                2.    condition 钥匙!
                3.    flag 标识! flag = 1 AA  2 BB  3 CC 
                4.    flag 的值修改!
    
    
    2.    并发容器类!
            List 集合!
                在多线程的时候,向集合容器中添加数据,则会报错!
                java.util.ConcurrentModificationException:并发修改异常!
                底层代码:
                    if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
    
                集合添加add();方法的底层源码!
                    public boolean add(E e) {
                        ensureCapacityInternal(size + 1);  // Increments modCount!!
                        elementData[size++] = e;
                        return true;
                    }
    
                解决方案:
                    1.    使用Vector 集合,线程安全的! add 方法有锁!
                            public synchronized boolean add(E e) {
                                modCount++;
                                ensureCapacityHelper(elementCount + 1);
                                elementData[elementCount++] = e;
                                return true;
                            }
    
                    2.    使用集合工具类!
                        List<String> list = Collections.synchronizedList(new ArrayList<>());
    
                            vector:**内存消耗比较大**,适合一次增量比较大的情况 ,同步方法!
                            SynchronizedList:**迭代器涉及的代码没有加上线程同步代码** 非迭代器部分,使用是同步代码块!
    
    
                    3.    CopyOnWriteArrayList 底层原理:写时复制技术,思想,读写分离!
    
    
                            add();
                            底层源码:
                            public boolean add(E e) {
                                final ReentrantLock lock = this.lock;
                                lock.lock();
                                try {
                                    Object[] elements = getArray();
                                    int len = elements.length;
                                    Object[] newElements = Arrays.copyOf(elements, len + 1);
                                    newElements[len] = e;
                                    setArray(newElements);
                                    return true;
                                } finally {
                                    lock.unlock();
                                }
                            }
    
                            使用场景:多读写少!
                                CopyOnWriteArrayList 获取数据的时候有锁么?写入数据的时候,需要创建一个副本!
    
                            缺点:
                                1.    需要足够大的内存!
                                2.    可能会出现数据不一致的问题! 保证的数据最终一致性!
    
                            在多线程操作的情况下:ArrayList 推荐使用 CopyOnWriteArrayList
    
            Set 集合 ,Map 集合!
                Set 集合不安全:
                    java.util.ConcurrentModificationException
                    解决方案:CopyOnWriteArraySet 类!
                    源码:
                            private final CopyOnWriteArrayList<E> al;
    
                            public boolean add(E e) {
                                return al.addIfAbsent(e);
                            }
    
                            public boolean addIfAbsent(E e) {
                                Object[] snapshot = getArray();
                                return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
                                    addIfAbsent(e, snapshot);
                            }
    
                            //    写时复制技术!
                            private boolean addIfAbsent(E e, Object[] snapshot) {
                                final ReentrantLock lock = this.lock;
                                lock.lock();
                                try {
                                    Object[] current = getArray();
                                    int len = current.length;
                                    if (snapshot != current) {
                                        // Optimize for lost race to another addXXX operation
                                        int common = Math.min(snapshot.length, len);
                                        for (int i = 0; i < common; i++)
                                            if (current[i] != snapshot[i] && eq(e, current[i]))
                                                return false;
                                        if (indexOf(e, current, common, len) >= 0)
                                                return false;
                                    }
                                    Object[] newElements = Arrays.copyOf(current, len + 1);
                                    newElements[len] = e;
                                    setArray(newElements);
                                    return true;
                                } finally {
                                    lock.unlock();
                                }
                            }
    
                Map:    集合!
                    java.util.ConcurrentModificationException
                    解决方案:
                        ConcurrentHashMap!
    
                        源码:
                                public V put(K key, V value) {
                                    return putVal(key, value, false);
                                }
    
                                重点:***** 同步代码块!
                                final V putVal(K key, V value, boolean onlyIfAbsent) {
                                    if (key == null || value == null) throw new NullPointerException();
                                    int hash = spread(key.hashCode());
                                    int binCount = 0;
                                    for (Node<K,V>[] tab = table;;) {
                                        Node<K,V> f; int n, i, fh;
                                        if (tab == null || (n = tab.length) == 0)
                                            tab = initTable();
                                        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                                            if (casTabAt(tab, i, null,
                                                         new Node<K,V>(hash, key, value, null)))
                                                break;                   // no lock when adding to empty bin
                                        }
                                        else if ((fh = f.hash) == MOVED)
                                            tab = helpTransfer(tab, f);
                                        else {
                                            V oldVal = null;
                                            synchronized (f) {
                                                if (tabAt(tab, i) == f) {
                                                    if (fh >= 0) {
                                                        binCount = 1;
                                                        for (Node<K,V> e = f;; ++binCount) {
                                                            K ek;
                                                            if (e.hash == hash &&
                                                                ((ek = e.key) == key ||
                                                                 (ek != null && key.equals(ek)))) {
                                                                oldVal = e.val;
                                                                if (!onlyIfAbsent)
                                                                    e.val = value;
                                                                break;
                                                            }
                                                            Node<K,V> pred = e;
                                                            if ((e = e.next) == null) {
                                                                pred.next = new Node<K,V>(hash, key,
                                                                                          value, null);
                                                                break;
                                                            }
                                                        }
                                                    }
                                                    else if (f instanceof TreeBin) {
                                                        Node<K,V> p;
                                                        binCount = 2;
                                                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                                                       value)) != null) {
                                                            oldVal = p.val;
                                                            if (!onlyIfAbsent)
                                                                p.val = value;
                                                        }
                                                    }
                                                }
                                            }
                                            if (binCount != 0) {
                                                if (binCount >= TREEIFY_THRESHOLD)
                                                    treeifyBin(tab, i);
                                                if (oldVal != null)
                                                    return oldVal;
                                                break;
                                            }
                                        }
                                    }
                                    addCount(1L, binCount);
                                    return null;
                                }
    
    
                    HashSet底层数据结构是什么?HashMap ?
                    set.add(value)  value 是 HashMap 的key,还是value?
                        key!
    
                    源码:
                        private transient HashMap<E,Object> map;
                        private static final Object PRESENT = new Object();
    
                        public boolean add(E e) {
                            return map.put(e, PRESENT)==null;
                        }
    
    3.    JUC强大的辅助类:
            a.    CountDownLatch(倒计数器)
                构造:
                    CountDownLatch(int count) //    初始化count 个数! 6;
                方法:
                    await() :阻塞线程用的!
                    countDown() :减少计数
                案例:
                    6个同学陆续离开教室后值班同学才可以关门!
    
                面试题:
                CountDownLatch 与 join 方法的区别?
                CountDownLatch:在运行的时候,可以调用await()方法! 调用了这个方法之后,CountDownLatch 并不一定会结束!
    
                Join:调用一个子线程的Join(); 这个线程会被阻塞,直到子线程执行完毕!
    
    
            b.    CyclicBarrier(循环栅栏)
                构造:
                    CyclicBarrier(int parties) 初始化值 屏障个数!
                    CyclicBarrier(int parties, Runnable barrierAction) 初始值,线程!
                        当达到一个屏障点的时候,触发一个线程操作!
    
                方法:
                    await() :阻塞线程用的!
    
                案例:组队打boss过关卡游戏!
    
                面试题:
                CyclicBarrier和CountDownLatch的区别?
                    CountDownLatch的计数器只能使用一次,    减少计数到达目的!
                    CyclicBarrier的计数器可以使用reset()方法重置,调用多次,    达到这个屏障才能触发线程!
    
            c.    Semaphore(信号量)
                构造:
                    Semaphore(int permits) :初始化值 指的是资源个数!
                    Semaphore(int permits, boolean fair)  初始化值,公平、非公平!
    
                方法:
                    acquire() :获取到资源!
                    release() :释放资源!
    
                    案例:6辆车抢占3个车位!
    
    4.    Callable接口
            Callable接口,在实现线程的有异常信息,同时还有返回值! 能够创建线程!
    
            面试题: Callable和Runable对比!
    
            Callable:实现call 方法,有返回值,有异常! isDone(), get();
            Runnable: 实现run 方法,没有返回值,没有异常!
    
    
            如何使用Callable接口!
                创建线程要找中间人!
                    FutureTask!
    
                前端的小妹妹,找一个合适的!
                    找班主任导师.....
    
                    拉跟线.....
    
            线程类构造:
                Thread(Runnable target);
                Thread(Runnable target, String name);
    
            根据线程类的构造:我们想办法让Callable 接口 与Runnable 接口有关系!
                Runnable 这个接口中有个 实现类 FutureTask!
                构造:
                FutureTask(Callable<V> callable);
    
            //   Runnable 这个接口中有个 实现类 FutureTask!
            //  FutureTask(Callable<V> callable);
            FutureTask futureTask = new FutureTask<>(new MyThread2());
            //  Thread(Runnable target);
            new Thread(futureTask).start();
    
            System.out.println(futureTask.get());
    
            get();    获取到返回值结果!通常都放在最后执行!并且它只执行一次!
            isDone();    表示判断当前任务是否执行完成!
    
            FutureTask:通常表示执行一个相对比较耗时,或者说未来任务!
    
            面试题:
                创建线程四种方式:
                    实现Runnable    没有返回值.
                    实现Callable    又返回值.
                    继承Thread    少用!Java 单继承! 慎用!
                    线程池    提高项目运行速度!
    
    5.    阻塞队列:
            回顾:
                栈:先进后出,后进先出
                        刷盘子!
    
                队列:先进先出
                        排队做核酸!
    
            阻塞队列:
                在多线程领域中,能够自动识别”任务“什么时候阻塞,什么唤醒! 多线程执行任务的时候,如果队列满了,则有新任务过来的时候,
                    它会自动阻塞,如果有线程处理完成一个任务之后,这个阻塞队列中的任务,则会被处理,后续的任务就能够进来了!
    
                特点:    必会!
                    1.    有任务进来的时候,当队列满了进行排队{阻塞}!
    
                    2.    当队列中为空的时候,线程获取任务时,则会发生阻塞!
    
    
                为什么需要BlockingQueue?
                    1.    高效!
    
            阻塞队列分类:了解!
                ArrayBlockingQueue: 由数组结构组成的有界阻塞队列。
                LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列。
                SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
    
    
            了解阻塞队列方法:
                put:
                take:
    
            阻塞队列使用场景:
                线程池中!
    

    JUC 第三天:
    回顾:
    1. 并发容器类:必会!
    ArrayList:CopyOnWriteArrayList
    HashSet:CopyOnWriteArraySet {CopyOnWriteArrayList}
    底层原理都是”写时复制”技术!
    HashMap:ConcurrentHashMap
    使用sync
    HashSet 添加数据的时候,add(value); 底层HashMap ,那么value 是作为 HashMap 的 key,来存储的!

            请你说一下对集合的认识:
                从集合的框架图:
                List , Set 区别? Set 无序不重复? set.add(new Stu(1,"张三"));  set.add(new Stu(2,"李四"));  
    
        2.    HUC 强大辅助类:了解
            1. CountDownLatch(倒计数器)
            2. CyclicBarrier(循环栅栏)
            3. Semaphore(信号量)
    
        3.    Callable 接口! 必会! 函数式接口!
                创建线程的方式! 4种!
                Callable 与 Runnable 区别?
                中间人:
                    FutureTask:用来执行未来任务!
    
                //    执行线程!
                new Thread(new FutureTask(new Callable)).start();
    
        4.    阻塞队列:
                特点:
                    1.    当队列中满了,再向队列中添加数据的时候,则会发生阻塞! put();
                    2.    当队列为空的时候,从队列中获取数据,则会发生阻塞!take();
    
                使用场景:线程池!
    

    重点:
    1. 线程池!

    2.    多线程并发底层原理!
    
    
    1.    线程池
            优势:线程复用,控制最大并发数,管理线程!
    
            需要记住的类:Executor,ExecutorService,ThreadPoolExecutor, Executors 这几个类。
    
            案例:如何创建线程池!Executors!
    
                a.    Executors.newCachedThreadPool();    表示创建一个可扩容的线程池!
                        public static ExecutorService newCachedThreadPool() {
                            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                                          60L, TimeUnit.SECONDS,
                                                          new SynchronousQueue<Runnable>());
                        }
    
                b.    Executors.newSingleThreadExecutor(); 创建一个线程池,但是这个池中就一个线程! 一池一线程!
                        public static ExecutorService newSingleThreadExecutor() {
                            return new FinalizableDelegatedExecutorService
                                (new ThreadPoolExecutor(1, 1,
                                                        0L, TimeUnit.MILLISECONDS,
                                                        new LinkedBlockingQueue<Runnable>()));
                        }
    
    
                c.    Executors.newFixedThreadPool(3);    指定线程池中有几个线程!
                        public static ExecutorService newFixedThreadPool(int nThreads) {
                            return new ThreadPoolExecutor(nThreads, nThreads,
                                                          0L, TimeUnit.MILLISECONDS,
                                                          new LinkedBlockingQueue<Runnable>());
                        }
    
                    最终代码:都会指向 ThreadPoolExecutor 这个类!
    
            线程池7个核心参数: *****
                【ThreadPoolExecutor】
                public ThreadPoolExecutor(
                              int corePoolSize,        //核心线程数    
                              int maximumPoolSize,    //最大线程数,这个值必须要大于等于1    
                              long keepAliveTime,    //空闲线程存活时间
                              TimeUnit unit,        //空闲线程存活时间单位
                              BlockingQueue<Runnable> workQueue,    //阻塞队列
                              ThreadFactory threadFactory,    //线程池工厂 ,通常都是默认!Executors.defaultThreadFactory()
                              RejectedExecutionHandler handler //拒绝策略 ,通常默认!new AbortPolicy(); 工作线程大于最大线程数的时候
                              ){
                                //    业务逻辑:
                              }
    
                    第六,七个个参数,可以不用给值!可以省略..... 都有自己的默认值!
    
                核心线程个数的设置?
                    n:CPU 的核数!
                    IO密集型:
                        2*n    
                    CPU密集型:
                        n+1
    
    
            线程池底层工作原理:    *****
                见图:
    
            自定义线程池案例:
                以后面试的时候问你创建线程池使用 哪种?    *****
                    Executors.newCachedThreadPool();    
                    Executors.newSingleThreadExecutor(); 
                    Executors.newFixedThreadPool(3);    
                    底层都是使用的ThreadPoolExecutor!
                答案都不用!    用了,可能会导致OOM!  创建线程的最大个数是 Integer 最大值,或者是队列没有限制无限存储!
                    需要根据业务自定义线程池!
                    用的都是自定义线程池!
    
                如何判定这个线程池能够执行最多任务是多少?
                    最大线程个数+阻塞队列个数!
    
    
                拒绝策略:RejectedExecutionHandler!
                    AbortPolicy(默认)    抛出异常!
                    CallerRunsPolicy:    没有异常,不会抛弃任务,让调用者去执行任务!
                    DiscardOldestPolicy:抛弃队列中等待最久的任务,不会抛出异常!
                    DiscardPolicy:该策略默默地丢弃无法处理的任务
    
                    自定义拒绝策略!
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    2,  //核心线程个数
                    5,  //最大线程个数
                    3L, //空闲线程存活时间
                    TimeUnit.SECONDS,   //空闲线程存活时间单位
                    new ArrayBlockingQueue<>(3),    //阻塞队列
                    Executors.defaultThreadFactory(), //默认线程池工厂
                    //  new ThreadPoolExecutor.DiscardPolicy()// 默认的拒绝策略!
                    new RejectedExecutionHandler() {
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                            System.out.println("别来人了.....");
                        }
                    }
            );
    
    2.    多线程并发底层原理
            a.    计算机运行:CPU ---> CPU 缓存 ----> 主内存!
            b.    Java内存模型{java memory model}
                    目的:使用同样的代码能够在不同的环境中运行出同样的效果!
    
                        本质上是没有关系的!
                        JMM:    主内存和工作内存
                        JVM:    堆,栈,方法区
    
                            工作内存:栈!
                            主内存:堆中对象实例部分!方法区
    
                    主内存对应的是硬件的物理内存,工作内存对应的是寄存器和高速缓存!
    
                    JMM 对主内存规定:*****
                        - 线程对共享内存的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写;
                        - 不同线程无法直接访问其他线程工作内存中的变量,因此共享变量的值传递需要通过主内存完成。
    
                    JMM 三大特性:*****
                        1.    原子性:
                                Integer a = 0; 不能分割的,原子性操作!
                                a++ 是可以分割的,a=a+1; 非原子性操作!多线程中存在安全隐患!解决方案使用锁,或者使用院子类解决!
    
                        2.    可见性:
                                当A 线程修改了主内存中的数据时,可以让B 线程可见!
                                在 Java 中 volatile、synchronized 和 final 实现可见性
    
                        3.    有序性:
                                多线程中,让线程读取顺序是有序的。为什么会出现无序的状况:{因为主内存与工作内存在传进的时候存在一定的延迟}
                                volatile:
                                synchronized:
    
            c.    volatile关键字 可见性:
    
                    //  可见性:
                    public class VolatileDemo {
    
                        //  声明一个变量:
                        private static volatile Integer flag = 1;
    
                        public static void main(String[] args) throws InterruptedException {
                            //  创建一个线程
                            new Thread(()->{
                                System.out.println("我是子线程:flag 的值:"+flag);
                                //  循环 如果子线程感知到flag = 2 就会结束,说明可见! 否则就不会结束!说明不可见!
                                while (flag==1){
    
                                }
                                System.out.println("子线程结束......");
                            }).start();
    
                            //  睡眠:
                            Thread.sleep(400);
                            flag=2;
                            System.out.println("主线程:flag 的值:"+flag);
                        }
                    }
    
                    后续代码省略....
                总结:volatile关键字,可见性 有序性,不具备原子性:
    
                1.    被volatile修改的变量,认为它是共享变量,可以被其他线程感知!比sync更轻量级的同步机制!
    
                2.    禁止重排!    
    
                3.    不保证原子性!
    
                4.    读性能可以,但是写的性能稍差!
    
    
            了解 Happen-Before 原则:
                概念:当A操作先行发生于B操作,则在发生B操作的时候,操作A产生的影响能被B观察到
                了解:Happen-Before的规则
    
        CAS:
            概念:Compare and Swap。比较并交换的意思。CAS操作有3个基本参数:内存地址A,旧值B,新值C
                if(内存地址A.equals(旧值B)){
                    新值C
                }else{
                    不能更新!
                }
            作用:CAS是解决多线程并发安全问题的一种乐观锁算法!
    
            // 对象、对象的属性地址偏移量{参数}、预期值、修改值
            public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    
    
            案例:    
                AtomicInteger:具有原子性的操作类!
    
            优点:AtomicInteger 原子性操作!在多线程情况下能够保证数据安全!
    
            缺点:
                1.    开销大!
                2.    ABA问题!
                3.    不能保证代码块具有原子性,只能保证的一个变量具有原子性!
        AQS:
    
            读写锁最大的缺点:很容易会产生'饥饿'状态! 读写锁的升级 :"StampLock" 能够解决锁的饥饿问题!扩展!
    
            通过AQS:可以实现独占锁!
    
            ReentrantLock 底层实现原理 就是 AQS 
                非公平锁时lock 方法:
                    //    锁方法
                    private Lock lock = new ReentrantLock();
                    lock.lock();
    
                    //    Sync 类下有个属性!
                    abstract void lock();
    
                    //    全局资源变量 默认值 0
                    private volatile int state;
    
                    //    属于:NonfairSync
                    final void lock() {
                        if (compareAndSetState(0, 1))    //    CAS把 state 设置为 1
                            setExclusiveOwnerThread(Thread.currentThread());  // 当前拥有锁的线程 获取到了锁!
                        else
                            acquire(1);    //    获取锁失败!
                    }
    
                    //    赋值当前拥有锁的线程方法!
                    protected final void setExclusiveOwnerThread(Thread thread) {
                        exclusiveOwnerThread = thread;
                    }
    
                    public final void acquire(int arg) {
                        if (!tryAcquire(arg) &&    //    表示再次尝试获取锁失败,
                            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  // 排队!
                            selfInterrupt();
                    }
    
                    protected final boolean tryAcquire(int acquires) {
                        return nonfairTryAcquire(acquires);
                    }
    
                    // acquires = 1
                    final boolean nonfairTryAcquire(int acquires) {
                        final Thread current = Thread.currentThread();    //    获取到当前线程
                        int c = getState();  // 初始化是0, 
                        if (c == 0) {
                            if (compareAndSetState(0, acquires)) { //  CAS 比较交换!获取到锁!
                                setExclusiveOwnerThread(current); //    获取锁成功,设置当前线程为获取到锁的线程!
    
                                return true;
                            }
                        }
                        //    如果获取到的c 不是0,则说明已经有锁了!
                        else if (current == getExclusiveOwnerThread()) {  //    可重入体现!拥有锁的线程与当前线程是一个线程!
                            int nextc = c + acquires;
                            if (nextc < 0) // overflow
                                throw new Error("Maximum lock count exceeded");
                            setState(nextc);
                            return true;
                        }
                        return false;
                    }    
    
                    //    这个方法是获取到当前拥有锁的线程是谁!
                    protected final Thread getExclusiveOwnerThread() {
                        return exclusiveOwnerThread;
                    }                    
    
                    //    解锁:
                    lock.unlock();
    
                    public void unlock() {
                        sync.release(1);
                    }
    
                    //    释放资源:
                    public final boolean release(int arg) {
                        if (tryRelease(arg)) {
                            Node h = head;
                            if (h != null && h.waitStatus != 0)
                                unparkSuccessor(h);
                            return true;
                        }
                        return false;
                    }
    
                    //    unlock 一次 从 state 中 -1 操作!
                    protected final boolean tryRelease(int releases) {
                        //    可重入锁 state = 2; unlock 一次! state = 1 再次调用unlock ,则state = 0;
                        int c = getState() - releases;
                        if (Thread.currentThread() != getExclusiveOwnerThread()) // 解锁的时候,必须是加锁的线程解锁!
                            throw new IllegalMonitorStateException();
                        boolean free = false;
                        if (c == 0) {
                            free = true;
                            setExclusiveOwnerThread(null); //    需要将拥有锁的线程的变量变为空!
                        }
                        setState(c);
                        return free;
                    }
            公平锁:
                FairSync 类!
    
                    final void lock() {
                        acquire(1);
                    }
    
                    public final void acquire(int arg) {
                        if (!tryAcquire(arg) &&    // 表示获取到锁!
                            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果没有获取到就排队
                            selfInterrupt();
                    }
    
                    protected boolean tryAcquire(int arg) {
                        throw new UnsupportedOperationException();
                    }
                    protected final boolean tryAcquire(int acquires) {
                        final Thread current = Thread.currentThread(); //    获取当前线程!
                        int c = getState(); // 获取state 状态!
                        if (c == 0) {
                            if (!hasQueuedPredecessors() &&  // 要做排队了!
                                compareAndSetState(0, acquires)) {
                                setExclusiveOwnerThread(current);
                                return true;
                            }
                        }
                        else if (current == getExclusiveOwnerThread()) {
                            int nextc = c + acquires;
                            if (nextc < 0)
                                throw new Error("Maximum lock count exceeded");
                            setState(nextc);
                            return true;
                        }
                        return false;
                    }
    
                    //    公平锁的核心代码! 按照顺序进行排队!
                    public final boolean hasQueuedPredecessors() {
                        // The correctness of this depends on head being initialized
                        // before tail and on head.next being accurate if the current
                        // thread is first in queue.
                        Node t = tail; // Read fields in reverse initialization order
                        Node h = head;
                        Node s;
                        return h != t &&
                            ((s = h.next) == null || s.thread != Thread.currentThread());
                    }
    
                    static final class Node {
                        ...
                    }
                    //    AbstractQueuedSynchronizer 类!有个Node 类,
                    volatile Node next;