• juc详解


    JUC并发

    线程通信

    package com.juc.lock;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 线程定制化通信
     */
    public class ThreadDemo3 {
        public static void main(String[] args) {
            ShareResource shareResource=new ShareResource();
            new Thread(()->{
                for (int i = 0; i < 9; i++) {
                    try {
                        shareResource.print5(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"a").start();
    
            new Thread(()->{
                for (int i = 0; i < 9; i++) {
                    try {
                        shareResource.print10(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"b").start();
    
            new Thread(()->{
                for (int i = 0; i < 9; i++) {
                    try {
                        shareResource.print15(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"c").start();
        }
    }
    
    class ShareResource{
        //标志位
        private int flag=1;
        private Lock lock=new ReentrantLock();
        private Condition c1=lock.newCondition();//条件 个人理解 锁程
        private Condition c2=lock.newCondition();
        private Condition c3=lock.newCondition();
    
        public void print5(int loop) throws InterruptedException {
            lock.lock();//枷锁
            try{
                //while防止虚假唤醒
                while (flag !=1){
                    c1.await();//等待
                }
                for (int i = 0; i < 4; i++) {
                    System.out.println(Thread.currentThread().getName()+"------"+i);
                }
                flag=2;//修改标志位
                c2.signal();//通知
            }finally {
                lock.unlock();
            }
        }
    
      
        public void print10(int loop) throws InterruptedException {
            lock.lock();
            try{
                //防止虚假唤醒
                while (flag !=2){
                    c2.await();
                }
                for (int i = 0; i < 9; i++) {
                    System.out.println(Thread.currentThread().getName()+"------"+i);
                }
                flag=3;//修改标志位
                c3.signal();//
            }finally {
                lock.unlock();
            }
        }
    
        public void print15(int loop) throws InterruptedException {
            lock.lock();
            try{
                //防止虚假唤醒
                while (flag !=3){
                    c3.await();
                }
                for (int i = 0; i < 14; i++) {
                    System.out.println(Thread.currentThread().getName()+"------"+i);
                }
                flag=1;//修改标志位
                c1.signal();//
            }finally {
                lock.unlock();
            }
        }
    }
    
    • 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
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    package com.juc.sync;
    
    /**
     * synchronized  线程通信
     */
    public class ThreadDemo1 {
        public static void main(String[] args) {
            Share share = new Share();
    
            new Thread(()->{
                for (int i = 1; i < 10; i++) {
                    try {
                        share.incr();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"a").start();
    
            new Thread(()->{
                for (int i = 1; i < 10; i++) {
                    try {
                        share.decr();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"b").start();
        }
    }
    class Share{
        private int number =0;
    
        public synchronized void incr() throws InterruptedException {
            while (number !=0){//如果计数不为0 则等待
                    this.wait();
                }
                number++;
                this.notify();//通知其他线程可以执行了
        }
    
        public synchronized  void decr() throws InterruptedException {
            while (number !=1){//如果计数不为1 则等待
                this.wait();//等待的同时 会释放锁 synchronized 让其他线程抢锁 特点 在哪里睡 在哪里醒
            }
            number--;
            this.notify();//通知其他线程可以执行了
        }
    }
    
    • 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
    package com.juc.lock;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * lock 线程通信
     */
    public class ThreadDemo2 {
    
        public static void main(String[] args) {
            Share share=new Share();
            new Thread(()->{
                for (int i = 1; i < 10; i++) {
                    try {
                        share.incr();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"a");
    
            new Thread(()->{
                for (int i = 1; i < 10; i++) {
                    share.decr();
                }
            },"b");
        }
    }
    class Share {
        private int number =0;
        //创建lock
    
        private Lock lock=new ReentrantLock();
        private Condition condition= lock.newCondition();
            public void incr() throws InterruptedException {
                //上锁
                lock.lock();
                try {
                    while(number !=0){
                        //等待
                        condition.await();
                    }
                    number++;
                    //通知其他线程可以抢方法了
                    condition.signalAll();
                }finally {
                    //解锁
                    lock.unlock();
                }
            }
    
        public void decr(){
            //上锁
            lock.lock();
            try {
                while(number !=1){
                    //等待
                    condition.await();
                }
                number--;
                //通知其他线程可以抢方法了
                condition.signalAll();
            }catch (Exception e){
            }finally {
                //解锁
                lock.unlock();
            }
        }
    }
    
    • 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

    解决集合不安全的问题

    package com.juc.lock;
    
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /* 锁是锁的对象
            线程不安全
     */
    public class ThreadDemo4 {
        public static void main(String[] args) {
            //ArrayList
    //        List list=new ArrayList();
    //            List list=new Vector();//Vector解决并发问题
    //        List list= Collections.synchronizedList(new ArrayList());//通过collections的synchronizedList解决并发
    //        List list=new CopyOnWriteArrayList();//juc并发包下
    //
    //        for (int i = 0; i < 30; i++) {
    //            new Thread(()->{
    //                list.add(UUID.randomUUID().toString().substring(0,8));
    //                System.out.println(list);
    //            },String.valueOf(i)).start();
    //        }
    
            //hashset
            // Set set=new hashset<>();
    //        Set set=new CopyOnWriteArraySet<>();
    //        for (int i = 0; i < 30; i++) {
    //            new Thread(()->{
    //                set.add(UUID.randomUUID().toString().substring(0,8));
    //                System.out.println(set);
    //            },String.valueOf(i)).start();
    //        }
    
            //hashMap
    //        Map map=new HashMap<>();
            Map<String,String> map=new ConcurrentHashMap<>();
            for (int i = 0; i < 30; i++) {
                String value = String.valueOf(i);
                new Thread(()->{
                    map.put(value,UUID.randomUUID().toString().substring(0,8));
                    System.out.println(map);
                },String.valueOf(i)).start();
            }
    
        }
    
    }
    
    • 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

    死锁的产生-

    两个线程互相争抢锁 导致都在等待

    a抢b的锁 b抢a的锁

    系统资源不足
    
    进程运行的推进顺序不对 
    
    资源分配不当
    
    • 1
    • 2
    • 3
    • 4
    • 5

    查看java进程

    jps -l

    jstack

    创建线程的方式

    继承Thread

    实现Runnable

    Callable接口

    线程池

    Runnable和Callable的区别

    是否有返回值

    公平锁和非公平锁

    公平锁 – 效率不高

    保证每一个线程都可以获取

    非公平锁–效率高

    可能会让一个线程跑完

    package com.juc.lock;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    /***
     * 买票
     */
    public class SaleTicket {
        public static void main(String[] args) {
             Ticket  ticket = new Ticket();
            new Thread(()-> {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }, "aa").start();
    
            new Thread(new Runnable() {
                public void run() {
                    for (int i = 0; i < 40; i++) {
                        ticket.sale();
                    }
                }
            }, "bb").start();
    
            new Thread(new Runnable() {
                public void run() {
                    for (int i = 0; i < 40; i++) {
                        ticket.sale();
                    }
                }
            }, "cc").start();
        }
    
    }
    class Ticket {
        private int number = 30;
        private final ReentrantLock lock=new ReentrantLock(true);
    
    
        public  void sale() {
            lock.lock();
            try{
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName() + ":卖出:" + number-- + "剩下" + number);
                }
            }catch (Exception e){
    
            }finally {
                lock.unlock();
            }
        }
    }
    
    • 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

    synchronized可重入锁

    public synchronized void add(){
        add();
    }
    
    • 1
    • 2
    • 3

    lock可重入锁

    可重入锁

    在锁中可以继续锁它本身

    package com.juc.sync;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 可重入锁 --递归锁
     */
    public class SyncLockDemo {
    
        public synchronized void add(){
            add();
        }
    
        public static void main(String[] args) {
            //synchronized 可重入锁
    //        new SyncLockDemo().add();
            //lock
            Lock lcok=new ReentrantLock();
            new Thread(()->{
                lcok.lock();
                try {
                    System.out.println("zzzzz1");
                    lcok.lock();
                    try {
                        System.out.println("zzzzz2");
                    }finally {
                        lcok.unlock();
                    }
                }finally {
                    lcok.unlock();
                }
    
            },"a").start();
    
        }
    }
    
    • 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

    Callable

    FutureTask 未来任务

    可以将在子线程获取的值 在主线程中获取出来
    
    • 1

    callable在runnable的基础上可以返回数据

    因为thread的构造方法中没有callable 需要使用它的实现类 futuretask来实现

    package com.juc.call;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    import java.util.concurrent.FutureTask;
    
    /**
     * 未来任务--callable的实现类
     */
    public class MyThread {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    //        new Thread(new MyThreadDemo(),"a").start();
    
    
            //FutureTask
            /**
             *      未来任务 可以将在子线程获取的值 在主线程中获取出来
             *
             */
    //        FutureTask future=new FutureTask(new MyThreadDemo2());
    //        new Thread(future,"b").start();
    
            FutureTask<Integer> futureTask2=new FutureTask(()->{
                System.out.println(Thread.currentThread().getName());
                return 200;
            });
            new Thread(futureTask2,"c").start();
    //       while (!futureTask2.isDone()){
    //           System.out.println("wait");
    //       }
            System.out.println(futureTask2.get());
            System.out.println(Thread.currentThread().getName());
    
    
        }
    }
    
    
    class MyThreadDemo implements Runnable{
        @Override
        public void run() {
    
        }
    }
    
    class MyThreadDemo2 implements Callable{
        @Override
        public Object call() throws Exception {
    
    
            return 200;
        }
    }
    
    • 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

    CountDownLatch

    减一计数 计数辅助类

    在创建了CountDownLatch对象后

    只要子线程中调用

    countDownLatch.countDown();
    
    • 1

    的次数少于new对象时设定的值

    那么 countDownLatch.await()会一直等待 直到扣减的数量是设定的值才会继续执行

    package com.juc.juc;
    
    import java.util.concurrent.CountDownLatch;
    
    /**
     * juc 计数辅助类 减少计数
     */
    public class CountDownLatchDemo {
    
        public static void main(String[] args) throws InterruptedException {
            //创建countdownlatch
            CountDownLatch countDownLatch=new CountDownLatch(6);
            for (int i = 0; i < 6; i++) {
    
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName());
                    //每次计数器减一
                    countDownLatch.countDown();
                },String.valueOf(i)).start();
            }
    
            countDownLatch.await();
            System.out.println("主"+Thread.currentThread().getName());
    
        }
    }
    
    • 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

    CyclicBarrierDemo

    循环栅栏

    在cyclicbarrier对象创建后 指定数量以及要执行的业务逻辑

    如果在多线程中 执行await的数量等于指定的数量则执行业务逻辑 否则子线程不进行下面的操作 以及不执行循环栅栏要执行的业务逻辑

    package com.juc.juc;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    /**
     * 循环栅栏 只有在满足填写对应线程数的等待值 才会执行runnable
     */
    public class CyclicBarrierDemo {
        private static final int NUMBER=7;
        public static void main(String[] args) {
            //参数一 设置线程数 参数二 达到七个线程数 进行操作
            CyclicBarrier cyclicBarrier=new CyclicBarrier(NUMBER,()->{
                System.out.println("集齐七颗龙珠 召唤神龙");
            });
            for (int i = 0; i < 7; i++) {
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"龙珠");
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                },String.valueOf(i)).start();
            }
        }
    }
    
    • 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

    Semaphore 信号灯

    在创建Semaphore对象时指定n个许可证的数量

    如果有多个线程抢占许可证 只有前n个线程能获取到许可证

    semaphore.acquire();//获取许可
    
    • 1

    其他没有获取到许可证的线程等待获取

    获取到许可证的线程执行

    semaphore.release();//释放许可
    
    • 1

    其他没有获取到许可的抢占这个许可

    package com.juc.juc;
    
    import java.util.Random;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 信号灯
     * 一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,
     * 每个acquire()都会阻塞,直到许可证可用,然后才能使用它。
     * 每个release()添加许可证,潜在地释放阻塞获取方。
     * 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行。
     */
    public class SemaphoreDemo {
    
    
        public static void main(String[] args) {
            //Semaphore 设置许可数量
            Semaphore semaphore=new Semaphore(3);
    
            for (int i = 0; i < 6; i++) {
                new Thread(()->{
                    try {
                        semaphore.acquire();//获取许可
                        System.out.println(Thread.currentThread().getName()+"获取许可");
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                        System.out.println(Thread.currentThread().getName()+"释放");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        semaphore.release();//释放许可
                    }
                },String.valueOf(i)).start();
            }
        }
    }
    
    • 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

    读写锁

    读锁 共享锁

    1线程修改时等待2线程读之后 2线程修改时等待1线程读之后

    写锁 独占锁

    读一起读 写一个人写

    1线程操作 第一条数据的同时 还要操作第二条数据

    2线程操作第二条数据的同时 还要操作第一条数据

    读写锁的使用

    `

    package com.juc.readWrite;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * 读写锁的使用
     *  当使用
     */
    public class ReadWriteLockDemo {
    
        public static void main(String[] args) throws InterruptedException {
            myCache myCache = new myCache();
            for (int i = 0; i < 5; i++) {
                final int num=i;
                new Thread(()->{
                        myCache.put(num+"",num+"");
                },String.valueOf(i)).start();
            }
            TimeUnit.MICROSECONDS.sleep(300);
            for (int i = 0; i < 5; i++) {
                final int num=i;
                new Thread(()->{
                    try {
                        System.out.println("读的值:"+myCache.get(num + ""));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                },String.valueOf(i)).start();
            }
        }
    }
    class myCache{
        //volatile 多线程操作时 可以让其他线程看到最新值 保证可见性 不保证原子性
        private volatile Map<String,Object> map=new HashMap<>();
        //读写锁对象
        private ReadWriteLock rwlock=new ReentrantReadWriteLock();
    
        public void put(String key,Object value) {
    //    rwlock.writeLock()//加写锁
    //       .lock();
            try{
                System.out.println(Thread.currentThread().getName()+"执行写操作");
                TimeUnit.MICROSECONDS.sleep(300);
                map.put(key,value);
                System.out.println(Thread.currentThread().getName()+"执行写操作完成");
            }catch (Exception e){
    
            }finally {
    //            rwlock.writeLock().unlock();
            }
    
    
        }
    
        public Object get(String key) throws InterruptedException {
    //         rwlock.readLock()//加读锁
    //        .lock();
            Object result=null;
            try{
                System.out.println(Thread.currentThread().getName()+"执行读操作");
                TimeUnit.MICROSECONDS.sleep(300);
                result=map.get(key);
            }catch (Exception e){
    
            }finally {
    //            rwlock.readLock().unlock();
            }
            return result;
        }
    }
    
    • 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
    • 75

    `

    当使用读写锁时,写锁时独占锁 只有一个线程写完 下一个线程才可以使用

    读锁是共享锁 ,所有线程可以同时访问

    volatile//多线程操作时 可以让其他线程看到最新值 保证可见性 不保证原子性
    
    • 1

    读写互斥 读读共享

    读写锁 一个资源被多个线程访问 或者 可以被一个写锁访问 但是不能同时存在 读写线程 读写互斥 读读共享

    无锁状态—多线程抢占资源–乱

    加锁–synchronized或者 reentrantlock 都是独占 每次只能来一个操作

    读写锁—reentrantReadwriteLock 读读可以共享 缺点–造成锁饥饿 只有读 没有写读的时候不能写 写的时候可以读

    读写锁的降级

    将写入锁降级为读锁

    流程

    获取写锁---获取读锁--释放写锁--释放读锁

    写的权限高于读的权限

    读锁不能升级为写锁

    只能写锁降级

    package com.juc.readWrite;
    
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * 锁降级
     */
    public class demo {
        public static void main(String[] args) {
            //可重入读写锁
            ReentrantReadWriteLock rw=new ReentrantReadWriteLock();
            ReentrantReadWriteLock.WriteLock writeLock = rw.writeLock();//写锁
            ReentrantReadWriteLock.ReadLock readLock = rw.readLock();//读锁
    
            //锁降级 获取写锁的同时 也可以获取读锁  如果先获取读锁 则无法获取写锁
            writeLock.lock();
            System.out.println("write");
            readLock.lock();
            System.out.println("read");
            writeLock.unlock();
            readLock.unlock();
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    阻塞队列

    a线程 往队列塞数据 如果队列满了 则阻塞 等待元素被移除

    b线程 从队列取数据 如果队列空了 则阻塞 等待元素被添加

    package com.juc.queue;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.TimeUnit;
    
    public class BlockingQueueDemo {
        public static void main(String[] args) throws InterruptedException {
            BlockingQueue blockingQueue=new ArrayBlockingQueue(3);
    
    //        System.out.println(blockingQueue.add("a"));
    //        System.out.println(blockingQueue.add("b"));
    //        System.out.println(blockingQueue.add("c"));
    //
    //        System.out.println(blockingQueue.remove());
    //        System.out.println(blockingQueue.remove());
    //        System.out.println(blockingQueue.remove());
    //        System.out.println(blockingQueue.remove());
    
    //        System.out.println(blockingQueue.offer("A"));
    //        System.out.println(blockingQueue.offer("b"));
    //        System.out.println(blockingQueue.offer("c"));
            System.out.println(blockingQueue.offer("d"));
    //        System.out.println(blockingQueue.poll());
    //        System.out.println(blockingQueue.poll());
    //        System.out.println(blockingQueue.poll());
    
    //        blockingQueue.put("A");
    //        blockingQueue.put("b");
    //        blockingQueue.put("c");
            blockingQueue.put("d");
    //
    //        System.out.println(blockingQueue.take());
    //        System.out.println(blockingQueue.take());
    //        System.out.println(blockingQueue.take());
    //        System.out.println(blockingQueue.take());
    
            blockingQueue.offer("a");
            blockingQueue.offer("b");
            blockingQueue.offer("c");
            blockingQueue.offer("c",3L,TimeUnit.SECONDS);
        }
    }
    
    • 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

    线程池

    package com.juc.pool;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 线程池
     */
    public class ThreadPoolDemo1 {
        public static void main(String[] args) {
            //一池多线程
            ExecutorService threadPool = Executors.newFixedThreadPool(5);
    //        try{
    //            for (int i = 0; i < 10; i++) {
    //                threadPool.execute(()->{//执行
    //                    System.out.println(Thread.currentThread().getName());
    //                });
    //            }
    //        }finally {
    //            threadPool.shutdown();//线程归还线程池
    //        }
            //一池一线程
            ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
    //        try{
    //            for (int i = 0; i < 10; i++) {
    //                threadPool2.execute(()->{//执行
    //                    System.out.println(Thread.currentThread().getName());
    //                });
    //            }
    //        }finally {
    //            threadPool2.shutdown();//线程归还线程池
    //        }
    
            //一池可扩容线程
            ExecutorService threadPool3 = Executors.newCachedThreadPool();
            try{
                for (int i = 0; i < 10; i++) {
                    threadPool3.execute(()->{//执行
                        System.out.println(Thread.currentThread().getName());
                    });
                }
            }finally {
                threadPool3.shutdown();//线程归还线程池
            }
    
        }
    }
    
    • 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

    ThreadPoolExecutor参数介绍

    int corePoolSize,//常驻线程数量 核心数量
    int maximumPoolSize,//  最大支持的线程数量
    long keepAliveTime,//线程的存活时间
    TimeUnit unit,//线程的存活时间
    BlockingQueue<Runnable> workQueue//阻塞队列 存放等待的请求
    ThreadFactory threadFactory,//线程工厂 用来创建线程
    RejectedExecutionHandler handler//拒绝策略 拒绝请求的处理
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    线程池的工作流程

    请求进入线程池 执行execute方法创建线程 前两个请求 在核心corepool下执行 后面三个请求阻塞在队列中

    最后进来的三个线程 如果核心线程和队列都满了 则在新创建的线程中执行 如果再来请求 并且超出线程池数量 则执行拒绝策略

    如果正在运行的线程数量小于coresize 则马上创建线程运行请求

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CY1M3jAI-1658366526352)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220708110430524.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fU91RjGB-1658366526353)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220708110446966.png)]

    自定义线程池

    package com.juc.pool;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 自定义线程池
     */
    public class ThreadPoolDemo2 {
        public static void main(String[] args) {
            ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,//常驻线程
                    5,//最大线程数量
                    2L,//线程存活的时间
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(3),//阻塞队列
                    Executors.defaultThreadFactory(),//默认创建线程的工厂
                    new ThreadPoolExecutor.AbortPolicy()//拒绝策略 抛异常
            );
                    try{
                for (int i = 0; i < 10; i++) {
                    poolExecutor.execute(()->{//执行
                        System.out.println(Thread.currentThread().getName());
                    });
                }
            }finally {
                        poolExecutor.shutdown();//线程归还线程池
            }
        }
    }
    
    • 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

    分支合并框架

    package com.juc.forkJoin;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.ForkJoinTask;
    import java.util.concurrent.RecursiveTask;
    
    public class forkJoinDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //MyTask
            MyTask myTask = new MyTask(0, 100);
            //创建分支合并池对象
            ForkJoinPool forkJoinPool=new ForkJoinPool();
            //提交任务到池中
            ForkJoinTask<Integer> task = forkJoinPool.submit(myTask);
            //返回结果
            Integer result = task.get();
            System.out.println(result);
            //关闭池
            forkJoinPool.shutdown();
        }
    }
    
    /**
     * RecursiveTask 分支合并的类
     */
    class MyTask extends RecursiveTask<Integer>{
    
        private static final Integer VALUE=10; //拆分计算值之差不超过10
        private int begin;//拆分开始值
        private int end;//拆分结束值
        private int result;//结束值
    
        public MyTask(int begin, int end) {
            this.begin = begin;
            this.end = end;
    
        }
    
        /**
         * 拆分逻辑
         * @return
         */
        @Override
        protected Integer compute() {
            //判断相加的两个值是否大于10
            if ((end-begin)<=VALUE){
                for (int i = begin; i <=end ; i++) {
                    result=result+i;
                }
            }else{//拆分
                int middle=(begin+end)/2;
                //中位数拆分
                MyTask myTask1 = new MyTask(begin,middle);
                MyTask myTask2 = new MyTask(middle+1,end);
                myTask1.fork();//拆分
                myTask2.fork();
                result=myTask1.join()+myTask2.join();//合并
            }
            return result;
        }
    }
    
    • 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

    异步回调

    package com.juc.CompletableFuture;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    /**
     * 异步回调
     */
    public class CompletableFutureDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    //        //无返回值
    //        CompletableFuture completableFuture=CompletableFuture.runAsync(()->{
    //            System.out.println(Thread.currentThread().getName());
    //        });
    //        completableFuture.get();//执行
            //有返回值
            CompletableFuture completableFuture2=CompletableFuture.supplyAsync(()->{
                System.out.println(Thread.currentThread().getName());
                return 200;
            });
            //获取回调结果
            completableFuture2.whenComplete((x,y)->{
                System.out.println(x+"---"+y);
                //x结果 y异常
            });
            completableFuture2.get();
        }
    }
    
    • 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

    概念 juc高级

    进程下有一个或者多个线程

    管程就是锁(也是监视器)

    6.4.10同步指令

    Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor,更常见的是直接将它称为“锁”)来实现的。
    方法级的同步是隐式的,无须通过字节码指令来控制,它实现在方法调用和返回操作之中。虚拟机可以从方法常量池中的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否被声明为同步方法。当方法调用时,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先成功持有管程,然后才膨执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程。在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的管程将在异常抛到同步方法边界之外时自动释放。
    同步一段指令集序列通常是由Java语言中的synchronized语句块来表示的,
    Java虚拟机的指令集中
    有monitorenter和monitorexit两条指令来支持synchronized关键字的语义,正确实现synchronized关键字需要Javac编译器与Java虚拟机两者共同协作支持

    用户线程和守护线程

    默认时用户线程

    用户线程是系统的工作线程

    守护线程

    后台做系统性的服务 例如 gc垃圾回收

    线程的daemon true 守护线程 false 用户线程

    如果用户线程全部结束意味着程序需要完成的业务操作已经结束了,守护线程随着JVM一同结束工作setDaemon(true)方法必须在start()之前设置,否则报llegalThreadStateException异常

    CompleTableFuture

    Future

    Future接口(FutureTask实现类)定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。
    I
    比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务后,
    主线程就去做其他事情了,忙其它事情或者先执行完,过了一会才去获取子任务的执行结果或变更的任务状态。

    Future接口可以为主线程开一个分支任务,专门为主线程处理耗时和费力的复杂业务

    引出futureTask

    Future是Java5新加的一个接口,它提供了一种异步并行计算的功能。
    如果主线程需要执行一个很耗时的计算任务,我们就可以通过future把这个任务放到异步线程中执行。主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。
    代码说话:
    Runnable接口Callable接口
    Future接口和FutureTask实现类
    目的:异步多线程任务执行且返回有结果,三个特点:多线程/有返回/异步任务(班长为老师去买水作为新启动的异步多线程任务且买到水有结果返回)

    runnable没有返回值 callable有返回值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-78kHYrMw-1658366526354)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220711110855040.png)]

    package com.juc;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * start源码
     */
    public class baseDemo {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
    
            FutureTask<String> futureTask=new FutureTask<String>(new myTask());
           
            Thread i=new Thread(futureTask,"a");
    
                i.start();
            System.out.println(futureTask.get());
        }
    }
    class myTask implements Callable<String>{
    
        @Override
        public String call() throws Exception {
            return "1123";
        }
    }
    
    • 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

    future结合线程池

    优点

    1 future+线程池异步多线程任务配合,能显著提高程序的执行效率。

    package com.juc.FutureAndPool;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.FutureTask;
    
    public class FutureThreadPoolDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService threadPool = Executors.newFixedThreadPool(3);
            long start = System.currentTimeMillis();
            FutureTask futureTask1=new FutureTask(()->{return "over1";});
            threadPool.submit(futureTask1);
            FutureTask futureTask2=new FutureTask(()->{return "over2";});
            threadPool.submit(futureTask1);
            FutureTask futureTask3=new FutureTask(()->{return "over3";});
            threadPool.submit(futureTask1);
    
            Object a = futureTask1.get();
            Object b = futureTask2.get();
            Object c = futureTask3.get();
    
            long end = System.currentTimeMillis();
            System.out.println(end-start);
    
            threadPool.shutdown();
        }
    }
    
    • 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

    缺点

    1 get()阻塞

    ​ 如果funture内部没有计算完 get()会一直等待结果计算完成

    ​ 一旦调用get()方法求结果,如果计算没有完成容易导致程序阻塞

    2 isDone

    package com.juc.FutureAndPool;
    
    import java.util.concurrent.*;
    
    public class FutureThreadPoolDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
            ExecutorService threadPool = Executors.newFixedThreadPool(3);
            long start = System.currentTimeMillis();
            FutureTask futureTask1=new FutureTask(()->{return "over1";});
            threadPool.submit(futureTask1);
            FutureTask futureTask2=new FutureTask(()->{return "over2";});
            threadPool.submit(futureTask1);
            FutureTask futureTask3=new FutureTask(()->{return "over3";});
            threadPool.submit(futureTask1);
            while(true){
                if (futureTask1.isDone()){
                    Object a = futureTask1.get(3,TimeUnit.SECONDS);
                    break;
                }else{
                    TimeUnit.SECONDS.sleep(3);
                }
            }
            while(true){
                if  (futureTask1.isDone()){
                    Object a = futureTask1.get(3,TimeUnit.SECONDS);
                    break;
                }else{
                    TimeUnit.SECONDS.sleep(3);
                }
            }
            while(true){
                if (futureTask1.isDone()){
                    Object a = futureTask1.get(3,TimeUnit.SECONDS);
                    break;
                }else{
                    TimeUnit.SECONDS.sleep(3);
                }
            }
            long end = System.currentTimeMillis();
            System.out.println(end-start);
    //        TimeUnit.SECONDS.sleep(5);
            threadPool.shutdown();
    
    
        }
    }
    
    • 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

    轮询的方式会耗费无谓的cPU资源,而且也不见得能及时地得到计算结牙如果想要异步获取结果,通常都会以轮询的方式去获取结果
    尽量不要阻塞

    Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果。

    耗费cpu

    优化思路

    回调通知

    应对Future的完成时间,完成了可以告诉我,也就是我们的回调通知通过轮询的方式去判断任务是否完成这样非常占CPU并且代码也不优雅

    创建异步任务

    future+线程池的配合

    想将多个异步任务的计算结果组合起来,后一个异步任务的计算结果需要前一个异步任务的值

    异步任务互相依赖

    将两个或多个异步计算合成一个异步计算,这几个异步计算互相独立,同时后面这个又依赖前一个处理的结果。

    计算速度最快

    当Future集合中某个任务最快结束时,返回结果,返回第一名处理结果。

    缺点

    使用Future之前提供的那点API就囊中羞涩,处理起来不够优雅,
    这时候还是让CompletableFuture以声明式的方式优雅的处理这些需求

    completableFuture 内容 源码分析

    public class CompletableFuture<T> implements Future<T>, CompletionStage<T> 
    
    • 1

    出现

    get()方法在Future 计算完成之前会一直处在阻塞状态下,isDone()方法容易耗费CPU资源,
    对于真正的异步处理我们希望是可以通过传入回调函数,在Future结束时自动调用该回调函数,这样,我们就不用等待结果。
    阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的CPU资源。因此,
    JDK8设计出CompletableFuture。
    CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5K33G2mn-1658366526354)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220712095532820.png)]

    completionStage

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iCfNPlid-1658366526355)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220712095418177.png)]

    代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,有些类似Linux系统的管道分隔符传参数。

    completableFuture 静态方法

    RunAsync --无返回值

    public static CompletableFuture<Void> runAsync(Runnable runnable) {
    
    • 1
    CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
        try{
            System.out.println(Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
        }
    });
    System.out.println(runAsync.get());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
    
    • 1
    • 2
    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
        try{
            System.out.println(Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
        }
    },threadPool);
    System.out.println(runAsync.get());
    threadPool.shutdown();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    SupplyAsync --有返回值

    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    
    • 1
    CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(()->{
                    try{
            System.out.println(Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
        }finally {
                        return "hello";
                    }
    });
    System.out.println(supplyAsync.get());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor) {
    
    • 1
    • 2
    CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(()->{
                    try{
            System.out.println(Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
        }finally {
                        return "hello";
                    }
    },threadPool);
    System.out.println(supplyAsync.get());
    threadPool.shutdown();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Executor参数说明

    没有指定Executor的方法,直接使用默认的ForkJoinPool.commonPool()作为它的线程池执行异步代码。

    如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码

    减少轮询和阻塞

    从Java8开始引入了CompletableFuture,它是Future的功能增强版,减少阻塞和轮询可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法

    异步编程

    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    CompletableFuture.supplyAsync(()->{
        int anInt = ThreadLocalRandom.current().nextInt(10);
        System.out.println(Thread.currentThread().getName());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return anInt;
    },threadPool).whenComplete((a,b)->{//whenComplet 二阶段结果
        if (b == null) {
            System.out.println("出结果了"+a);
        }
    }).exceptionally((b)->{//exceptionally 错误阶段
        System.out.println(b.getCause()+"错误");
        return null;
    });
    System.out.println(Thread.currentThread().getName()+"主");
    threadPool.shutdown();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    异步任务结束时,会自动回调某个对象的方法;

    主线程设置好回调后,不再关心异步任务的执行,

    异步任务之间可以顺序执行异步任务出错时,会自动回调某个对象的方法;

    链式语法和join

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CgSOiCKA-1658366526356)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220712103458278.png)]

    Lambda表达式+Stream流式调用+Chain链式调用+Java8函数式编程

    Runnable

    Runnable 我们已经说过无数次了,无参数,无返回值

    Function

    Function接受一个参数,并且有返回值

    Consumer

    Consumer接受一个参数,没有返回值

    BiConsumer

    BiConsumer接受两个参数(Bi,英文单词词根,代表两个的意思),没有返回值

    Supplier

    Supplier没有参数,有一个返回值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GzsrMta-1658366526357)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220712103805084.png)]

    join和get的区别

    join不在运行时做检查

    业务场景

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sLpm55qA-1658366526358)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220712104902845.png)]

    package com.juc.FutureAndPool;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.experimental.Accessors;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ThreadLocalRandom;
    import java.util.stream.Collectors;
    
    /**
     *
     */
    public class CompletableFutureMallDemo {
        static List<NetMall> list= Arrays.asList(new NetMall("jd"),
                new NetMall("dangdang"),
                new NetMall("taobao"));
    
        /**
         * 正常流处理
         * @param list
         * @param productName
         * @return
         */
        public static List<String> getPrice(List<NetMall> list,String productName){
            return list
                    .stream()
                    .map(netMall ->
                    String.format(productName + "in %s price is %.2f"
                            , netMall.getNetMallName(),
                            netMall.calcPrice(productName)))
                    .collect(Collectors.toList());
    
        }
    
        /**
         * 加入completablefuture
         * @param list
         * @param productName
         * @return
         */
        public static List<String> getPriceCompletableFuture(List<NetMall> list,String productName){
            return list
                    .stream()
                    .map(netMall ->
                            CompletableFuture.supplyAsync(() -> String.format(productName + "in %s price is %.2f"
                    , netMall.getNetMallName(),
                    netMall.calcPrice(productName))))
                    .collect(Collectors.toList())
                    .stream()
                    .map(s -> s.join())
                    .collect(Collectors.toList());
        }
        public static void main(String[] args) {
            long a = System.currentTimeMillis();
            List<String> mysql = getPrice(list, "mysql");
            for (String element : mysql) {
                System.out.println(element);
            }
            long b = System.currentTimeMillis();
            System.out.println(b-a);
        }
    }
    //@NoArgsConstructor
    //@AllArgsConstructor
    //@Data
    //@Accessors(chain = true)//启用链式
    class NetMall{
        @Getter
        private String NetMallName;
    
        public NetMall(String netMallName) {
            NetMallName = netMallName;
        }
        public double calcPrice(String productName){
            double aDouble = ThreadLocalRandom.current().nextDouble();
            return aDouble*2+productName.charAt(0);
        }
    }
    
    • 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
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    CompletableFuture常用方法

    获得结果和触发计算

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HkJULm0q-1658366526358)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220712112537104.png)]

    getNow()

    没有计算完成的情况下,给我一个替代结果
    计算完,返回计算完成后的结果
    立即获取结果不阻塞
    没算完,返回设定的valuelfAbsent值

    complete

    是否打断get方法立即返回括号值

    
    public class CompletableFutureDemo2{
    public static void main(String[] args) throws ExecutionExceptionInterruptedException{
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }return 533;
    });
    //注释掉暂停线程,get还没有算完只能返回complete方法设置的444,暂停2秒钟线程,异步线程能够计算完成返回gettry { TimeUnit.SECONDs.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
    //当调用CompletableFuture.get()被阻塞的时候, complete方法就是结京阻塞并get()获取设置的complete里面的值.System.out.println(completableFuture.complete(444)+"It"+completableFuture.get());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    / / system.out.printLn( completableFuture.get( ) );
    l /system.out.println(completableFuture.get(2L , TimeUnit.SECONDS) ) ;/ l system.out.printLn( completableFuture.join( ));
    //暂停几秒钟线程
    try { TimeUnit.SECoNDS.sleep( timeout: 2); } catch (InterruptedException e) { e.printStackTrace(); }/ / system.out.printLn( completableFuture.getNow( "xxx") );
    system.out.println(completableFuture.complete( value : "completevalue" )+"\)t"+completableFuture.join());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对计算结果进行处理

    thenapply

    计算结果存在依赖关系,这两个线程串行化
    异常相关
    由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停。

    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    CompletableFuture.supplyAsync(()->{
                try{
                                TimeUnit.SECONDS.sleep(1);
                            }catch (Exception e){
                                e.printStackTrace();
                            }
        System.out.println("1");
                return 200;
            }, threadPool)
    .thenApply((f)->{//针对上一步的结果进行进一步的计算
        System.out.println("2");
            return f+2;
    }).thenApply((f)->{
        System.out.println("3");
        return f+2;
    })
    .whenComplete((e,f)->{//获取回调结果
        if (f == null) {
            System.out.println("计算结果"+e);
        }
    }).exceptionally(e->{//捕捉异常
        return null;
    });
    System.out.println("主");
    
    threadPool.shutdown();
    
    • 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
    handle

    计算结果存在依赖关系,这两个线程串行化
    异常相关

    有异常也可以往下一步走,根据带的异常参数可以进一步处理

    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    CompletableFuture.supplyAsync(()->{
                try{
                                TimeUnit.SECONDS.sleep(1);
                            }catch (Exception e){
                                e.printStackTrace();
                            }
        System.out.println("1");
                return 200;
            }, threadPool)
    .handle((f,v)->{//针对上一步的结果进行进一步的计算
        System.out.println("2");
            return f+2;
    }).handle((f,v)->{
        System.out.println("3");
        return f+2;
    })
    .whenComplete((e,f)->{//获取回调结果
        if (f == null) {
            System.out.println("计算结果"+e);
        }
    }).exceptionally(e->{//捕捉异常
        return null;
    });
    System.out.println("主");
    
    threadPool.shutdown();
    
    • 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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kIdNBTPQ-1658366526359)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220714130121080.png)]

    计算结果进行消费

    thenAccept

    接收任务的处理结果,并消费处理,无返回结果

    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    CompletableFuture.supplyAsync(()->{
                 try{
                                 TimeUnit.SECONDS.sleep(1);
                             }catch (Exception e){
                                 e.printStackTrace();
                             }
                 return 200;
             },threadPool )
            .thenApply(s->{return s+ 2;})
            .thenAccept(System.out::println);
    threadPool.shutdown();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    区别

    thenRun(Runnable runnable)
    thenRun
    任务A执行完执行B,并且B不需要A的结果thenAccept(Consumer action)
    thenAccept
    任务A执行完执行B,B需要A的结果,但是任务B无返回值thenApply(Function fn)
    thenApply
    任务A执行完执行B,B需要A的结果,同时任务B有返回值

    system.out.println(CompletableFuture.supplyAsync(() ->"resultA" ). thenRun(() -> (}).join());
    system.out. println(CompletableFuture.supplyAsync(() -> "resultaA").thenAccept(r -) System,out . println(r) .join();system.out.println(CompletableFuture.supplyAsync(() -> "resultA" ).thenApply(r -> r + “result8B").join();
    
    
    • 1
    • 2
    • 3

    CompletableFututre和线程池的选择

    以thenRun和thenRunAsync为例,有什么区别?

    1没有传入自定义线程池,都用默认线程池ForkJoinPool;
    2传入了一个自定义线程池,
    如果你执行第一个任务的时候,传入了一个自定义线程池:
    调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。
    调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池
    3备注
    有可能处理太快,系统优化切换原则,直接使用main线程处理
    中,
    其它如: thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也是同理

    对计算速度的选用

    package com.juc.FutureAndPool;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.TimeUnit;
    
    public class CompletableFututreFastDemo {
        public static void main(String[] args) {
            CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return 1;
            });
            CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return 2;
            });
            //applyToEither CompletableFuture 哪个快 执行哪个
            CompletableFuture<String> future = a.applyToEither(b, x -> {
                return x + " in winner";
            });
    
            System.out.println(future.join());
        }
    }
    
    • 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

    对结算结果进行合并

    两个CompletionStage任务都完成后,最终能把两个任务的结果一起交给thenCombine来处理

    先完成的先等着,等待其它分支任务

    package com.juc.FutureAndPool;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.TimeUnit;
    
    public class CompletableFutureCombineDemo {
        public static void main(String[] args) {
            CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName());
                 try{
                                 TimeUnit.SECONDS.sleep(1);
                             }catch (Exception e){
                                 e.printStackTrace();
                             }
                return 1;
            });
            CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName());
                try{
                    TimeUnit.SECONDS.sleep(2);
                }catch (Exception e){
                    e.printStackTrace();
                }
                return 2;
            });
            /**
             * thenCombine 合并任务
             */
            CompletableFuture<Integer> completableFuture = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
                return x + y;
            });
            System.out.println(completableFuture.join());
    
        }
    }
    
    • 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

    线程锁

    悲观锁

    认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。
    synchronized关键字和lock的实现类都是悲观锁

    乐观锁

    认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。
    在Java中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。
    如果这个数据没有被更新,当前线程将自己修改的数据成功写入。
    如果这个数据已经被其它线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等
    判断规则
    1版本号机制Version
    ⒉最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。

    适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

    乐观锁则直接去操作同步资源,是一种无锁算法,得之我幸不得我命,再努力就是一句话:
    佛系锁

    /[=============悲观锁的调用方式public synchronized void m1(){/加锁后的业务逻辑......}
    //保证多个线程使用的是同一个Lock对象的前提下ReentrantLock lock = new ReentrantLock();public void m2() {
    lock. lock();try i
    //操作同步资源}finally i
    lock.unlock();
    l/=============乐观锁的调用方式
    //保证多个线程使用的是同一个AtomicInteger
    private AtomicInteger atomicInteger = new AtomicInteger();atomicinteger.incrementAndGet();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    锁的案例演示

    7.【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能
    锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
    说明︰尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC
    方法。

    synchronized 锁的是当前new的对象

    static synchronized 锁的是当前的类文件

    1标准访问有ab两个线程,请问先打印邮件还是短信

    2 sendEmail方法中加入暂停3秒钟,请问先打印邮件还是短信*

    一个对象里面如果有多个synchronized方法,某一个时刻外,只要一个线程去调用其中的一个synchronized方法了其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法
    锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法

    3添加一个普通的hello方法,请问先打印邮件还是helLo

    4有两部手机,请问先打印邮件还是短信

    加个普通方法后发现和同步锁无关
    换成两个对象后,不是同一把锁了,情况立刻变化。

    5有两个静态同步方法,有1部手机,请问先打印邮件还是短信

    6有两个静态同步方法,有2部手机,请问先打印邮件还是短信

    三种 synchronized锁的内容有一些差别:
    对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同
    把锁—>实例对象本身,
    对于静态同步方法,锁的是当前类的CLass对象,如Phone.class唯一的一个模板
    对于同步方法块,锁的是synchronized括号内的对象

    7 有1个静态同步方法,有1个普通同步方法,有1部手机,请问先打印邮件还是短信

    8有1个静态同步方法,有1个普通同步方法,有2部手机,请问先打印邮件还是短信

    当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。*
    所有的普通同步方法用的都是同一把锁—实例对象本身,就是new出来的具体实例对象本身,本类this
    也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。

    所有的静态同步方法用的也是同一把锁—类对象本身,就是我们说过的唯一模板cLass
    具体实例对象this和唯一模板cLass,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竟态条件的
    但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。|

    类加载是一个将class字节码文件实例化成Class对象并进行相关初始化的过程。
    全小写的class是关键字用来定义类,首字母大写的Class它是所有class的类。

    synchronized 字节码分析

    体现:

    作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁;

    作用于代码块,对括号里配置的对象加锁。

    作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;

    详细查看脑图

    公平锁和非公平锁(周阳)

    公平锁
    是指多个线程按照中请锁的顺序来获取锁,这里类似排队买票,先来的人先买后来的人在队尾排着,这是公平的Lock lock = new ReentrantLock(true);//true表示公平锁,先来先得
    非公平锁
    是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后中请的线程比先中请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转或者饥饿的状态(某个线程一直得不到锁)
    Lock lock = new ReentrantLock(false);//false表示非公平锁,后来的也可能先获得锁Lock lock = new ReentrantLock();//默认非公平锁

    递归锁(周阳)

    是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。

    如果是1个有 synchronized 修饰的递归调用方法,程序第2次进入被自己阻塞了岂不是天大的笑话,出现了作茧自缚。
    所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

    隐式锁(即synchronized关键字使用的锁)默认是可重入锁

    指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
    简单的来说就是:在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的
    与可重入锁相反,不可重入锁不可递归调用,递归调用就发生死锁。 
    
    • 1
    • 2
    • 3

    Synchronized的重入的实现机理√

    每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。
     
    当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。
     
    在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。
     
    当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    显式锁(即Lock)也有ReentrantLock这样的可重入锁。

    递归锁的意思就是在同步方法中如果有其他同步方法的调用 则内部同步方法可以直接获取锁 而不是等待外部锁释放再获取

    死锁的排查和命令

    系统资源不足
    进程运行推进的顺序不合适

    资源分配不当

    jps -l

    jstack 进程号

    jconsole

    objectManager和synchronied总结

    指针指向monitor对象l(也称为管程或监视器锁)的起始地址。每个对象都存在着一个monitor与之关联,当一个monitor被某个线程持有后,它便处于锁定状态。在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码
    ObjectMonitor.hpp文件,C++实现的)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VF7v79XY-1658366526364)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220718112128015.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HH7ca7cc-1658366526365)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220718112313831.png)]

    中断机制之中断协商机制

    首先
    一个线程不应该由其他线程来强制中断或停止,而是应该山线程自己自行停止,自己来决定自己的命运。所以,Thread.stop,Thread.suspend, Thread.resume都已经被废弃了。
    其次
    在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的协商机制——中断,也即中断标识协商机制。
    中断只是一种协作协商机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。
    若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true;接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程请求这条线程中断,
    此时究竟该做什么需要你自己写代码实现。
    每个线程对象中都有一个中断标识位,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断;通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。

    api说明

    public void interrupt() 实例方法,
    实例方法interrupt()仅仅是设置线程的中断状态为true,不会停止线程

    public static boolean interrupted() 静态方法,Thread.interrupted();
    判断线程是否被中断,并清除当前中断状态
    这个方法做了两件事:
    1 返回当前线程的中断状态
    2 将当前线程的中断状态设为false

    这个方法有点不好理解,因为连续调用两次的结果可能不一样。

    public boolean isInterrupted() 实例方法,
    判断当前线程是否被中断(通过检查中断标志位)

    通过volatile实现中断线程的停止

    package com.juc.FutureAndPool;
    
    import java.util.concurrent.TimeUnit;
    
    public class InterruptDemo {
        static volatile boolean isStop=false;
    
        public static void main(String[] args) {
            new Thread(()->{
                while(true){
                    if (isStop){
                        System.out.println( Thread.currentThread().getName()+"停止");
                        break;
                    }
                    System.out.println(",,,");
                }
    
            },"a").start();
    
             try{
                             TimeUnit.SECONDS.sleep(3);
                         }catch (Exception e){
                             e.printStackTrace();
                         }
    
            new Thread(()->{
                isStop=true;
            },"b").start();
        }
    
    }
    
    • 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

    通过AtomicBoolean实现线程中断停止

    package com.juc.FutureAndPool;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class InterruptDemo {
        static volatile boolean isStop=false;
        static AtomicBoolean atomicBoolean=new AtomicBoolean(false);
        public static void main(String[] args) {
            new Thread(()->{
                while(true){
                    if (atomicBoolean.get()){
                        System.out.println( Thread.currentThread().getName()+"停止");
                        break;
                    }
                    System.out.println(",,,");
                }
    
            },"a").start();
    
             try{
                             TimeUnit.SECONDS.sleep(3);
                         }catch (Exception e){
                             e.printStackTrace();
                         }
    
            new Thread(()->{
    //            isStop=true;\
                atomicBoolean.set(true);
            },"b").start();
        }
    
    }
    
    • 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

    通过thread自带的api实现中断

    在需要中断的线程中不断监听中断状态,
    一只发生中断,就执行相应的中断处理业务逻辑stop线程

    Thread a = new Thread(() -> {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {//判断线程是否中断
                        System.out.println(Thread.currentThread().getName() + "停止");
                        break;
                    }
                    System.out.println(",,,");
                }
    
            }, "a");
            a.start();
    
            try{
                TimeUnit.SECONDS.sleep(3);
            }catch (Exception e){
                e.printStackTrace();
            }
    
            new Thread(()->{
    //            isStop=true;\
    //            atomicBoolean.set(true);
    //            isStop2=true;
                a.interrupt();//中断线程
            },"b").start();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    interrupt和isinterrupted源码解析

    public void interrupt()中断此线程。
    除非当前线程正在中断〈始终允许),否则将调用此线程的checkAccess方法,这可能会导致抛出SecurityException 。
    如果该线程阻塞的调用wait() , wait(long),或wait(long,int)的方法Object类,或的join(), join(long), join
    long, int) , sleep(long),或sleep(long, int),这个类
    的方法,那么它的中断状态将被清除,并且将收到InterruptedException 。
    如果在InterruptibleChannel上的/O操作中阻止该线程,则通道将关闭,线程的中断状态将被设置,线程将收到ClosedByInterruptException 。如果该线程在Selector中被阻塞,则姓线程的中断状态将被设置,它将立即从选择操作返回,可能具有非零值,就像调用选择器的wakeup方法一样。
    如果以前的条件都不成立,则将设置该线程的中断状态。
    中断不活动的线程不会产生任何影响。

    具体来说,当对一个线程,调用interrupt()时:
    如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。
    所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行。
    ②如果线程处于被阻塞状态(例如处于sleep, wait, join等状态),在别的线程中调用当前线程对象的interrupt方法.那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。

    中断协商深度解析

    Thread t1= new Thread(()->{
        for (int i = 0; i < 300; i++) {
            System.out.println("for。。。"+i);
        }
        System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().isInterrupted());
    },"a");t1.start();
    
     System.out.println(t1.isInterrupted()+"--中断前的标识");
    
     t1.interrupt();
    
     System.out.println("中断后的标识 ---> "+t1.isInterrupted());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    打上中断标志后 不会停止线程

    如果线程已经结束 则isInterrupted()是fasle 因为线程不活动 不会产生影响

    如果线程中加了sleep之类的方法 除了其他线程需要调用中断方法 还需要在本线程中sleep之后再调用一次

    sleep方法抛出InterruptedException后,中断标识也被清空置为false,我们在catch没有通过调用th.interrupt()方法再次将中断标识置为true,这就导致无限循环了

    中断只是一种协商机制,修改中断标识位仅此而已,不是立刻stop打断

    静态方法interrupted

    判断线程是否被中断并清除当前中断状态。[这个方法做了两件事:
    1返回当前线程的中断状态,测试当前线程是否已被中断
    2将当前线程的中断状态清零并重新设为false,清除线程的中断状态
    此方法有点不好理解,如果连续两次调用此方法,则第二次调用将返回false,因为连续调用两次的结果可能不一样

    静态进去是否需要清除是true

    静态方法interrupted将会清除中断状态(传入的参数ClearInterrupted为true) ,
    实例方法islnterrupted则不会(传入的参数ClearInterrupted为false)。

    LockSupport

    用于创建锁和其他同步类的基本线程阻塞原语。

    LockSupport中的park()和 unpark()的作用分别是阻塞线程和解除阻塞线程

    static objectgetBlocker ( Thread t)
    返回提供给尚未解除阻塞的park方法的最新调用的阻止程序对象,如果未阻止,则返回null。
    static void
    park ()
    除非许可证可用,否则禁用当前线濯以进行线程调度。
    static void
    park(object blocker)
    除非许可证可用,否则禁用当前线程以进行线程调度。
    static void
    parkNanos (long nanos)
    除非许可证可用,否则禁用当前线程以进行线程调度,直到指定的等待时间。
    static void
    parkNanos(object blocker,long nanos)
    除非许可证可用,否则禁用当前线程以进行线程调度,直到指定的等待时间。
    static void
    parkUntil(long deadline)
    除非许可证可用,否则禁用当前线程以进行线程调度,直到指定的截止时间。
    static void
    parkUntil(object blocker,long deadline)
    除非许可证可用,否则禁用当前线程以进行线程调度,直到指定的截止时间。
    static void
    unpark(Thread thread)
    如果给定线程尚不可用,则为其提供许可。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    线程等待唤醒机制

    方式1:使用object中的wait()方法让线程箸待,使用object中的notify()方法唤醒线程

    方式2:使用Juc包中Condition的await()方法让线程等待,使用signal()方法唤醒线程

    方式3:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

    synchronized

    Object LockObject=new Object();
    
    
    new Thread(()->{
        synchronized (LockObject){
            System.out.println("进入a");
            try {
                LockObject.wait();//释放锁 并让当前线程陷入等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("又进入a了");
        }
    },"a").start();
    
    
    new Thread(()->{
        synchronized (LockObject){
            System.out.println("进入b");
            try {
                LockObject.notify();//释放锁 唤醒其他线程
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    },"b").start();
    
    • 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

    如果使用wait和notify 没有加synchronized 则报错

    如果wait和notify调用顺序颠倒 会阻塞线程

    wait和notify方法必须要在同步块或者方法里面,且成对出现使用
    先wait后notify才oK

    condition

    Lock lock=new ReentrantLock();
            Condition condition = lock.newCondition();
            new Thread(()->{
                lock.lock();
                    System.out.println("进入a");
                    try {
                        condition.await();//并让当前线程陷入等待 
                        System.out.println("又进入a了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        lock.unlock();//释放锁
                }
            },"a").start();
    
    
            new Thread(()->{
    //            synchronized (LockObject){
                lock.lock();
                    try {
                       condition.signal();// 唤醒其他线程
                        System.out.println("进入b");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally {lock.unlock();//释放锁
                }
            },"b").start();
    
    • 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

    如果去掉lock和unlock 则会报错

    在lock、unlock对里面
    才能正确调用condition中线程等待和唤醒的方法

    Condtion中的线程等待和唤醒方法,需要先获取锁一定要先await后signal,不要反了

    线程先要获得并持有锁,必须在锁块(synchronized或lock)中必须要先等待后唤醒,线程才能够被唤醒

    park和unpark

    permit许可证默认没有不能放行,所以一开始调park()方法当前线程就会阻塞,直到别的线程给当前线程的发放permit,park方法才会被唤醒。

    调用unpark(hread)方法后,就会将thread线程的许可证permit发放,会自动唤醒park线程,即之前阻塞中的LockSupport.park()方法会立即返回。

    sleep方法3秒后醒来,执行park无效,没有阻塞效果,解释如下先执行了unpark(t1)导致上面的park方法形同虚设无效,时间样

    Thread t1=   new Thread(()->{
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("wait");
           LockSupport.park();
        System.out.println("yes");
       },"a");t1.start();
    
    
       new Thread(()->{
           System.out.println("talk");
           LockSupport.unpark(t1);
       },"b").start();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    许可证只有一个

    LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
    LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞
    阻塞之后也有对应的唤醒方法。归根结
    底,LockSupport调用的Unsafe中的native代码。
    LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程LockSupport和每个使用它的线程都有一个许可(permit)关联。
    每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累凭证。
    形象的理解:
    线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。当调用park方法时
    如果有凭证,则会直接消耗掉这个凭证然后正常退出;*如果无凭证,就必须阻塞等待凭证可用;
    而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效。

    为什么可以突破wait/notify的原有调用顺序?
    因为unpark获得了一个凭证,之后再调用park方法,就可以名正言顺的凭证消费,
    故不会阻塞。
    先发放了凭证后续可以畅通无阻。
    为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?
    因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证;而调用两次park却需要消费两个凭证,证不够,不能放行。

    Java内存模型 JMM

    JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在它仅仅描述的是一组约定或规范,通过这绞规范定义了程序中(尤其是多线程)各个变量的读写访问方式并抉定一个线程对共享变量的写入何时以及如何变成对另一个线程可见,键技术点都是围绕多线程的原子性、可见性和有序性展开的。
    原则:
    JMM的关键技术点都是围绕多线程的原子性、可见性和有序性展开的
    能干嘛?
    1通过JMM来实现线程和主内存之间的抽象关系。
    2屏蔽各个硬件平台和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到一致的内存访问效果。

    可见性
    I
    是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道该变更,JMM规定了所有的变量都存储在主内存中。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMSF4jJo-1658366526365)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220720111812607.png)]

    系统主内存共享变量数据修改被写入的时机是不确定的,多线程并发下很可能出现"脏读",所以每个线程都有自己的工作内存、线程自己的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取,赋值等)都必需在线程自己的工作内存中进行,而不能够直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成

    原子性

    指一个操作是不可打断的,即多线程环境下,操作不能被其他线程干扰

    有序性

    对于一个线程的执行代码而言,我们总是习惯性认为代码的执行总是从上到下,有序执行。但为了提升性能,编译器和处理器通常会对指令序列进行重新排序。Java规范规定JVM线程内部维持顺序化语义,即只要程序的最终结果与它顺序化执行的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序。

    优缺点
    JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。但是,
    指令重排可以保证串行语义一致,但没有义务保证多线程间的语义也一致(即可能产生"脏读""),简单说,
    两行以上不相干的代码在执行的时候有可能先执行的不是第一条,不见得是从上到下顺序执行,执行顺序会被优化。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Rpdm5Xk-1658366526366)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220720112717098.png)]

    单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。

    处理器在进行重排序时必须要考虑指令之间的数据依赖性
    多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。

    多线程对变量的读写过程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zpbPpxkC-1658366526366)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220720134730196.png)]

    由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到的线程自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图:

    总结

    我们定义的所有共享变量都储存在物理主内存东
    每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
    线程对共享变量所有的操作都必须先在线程自己的工作内存中进行后写回主内存,不能直接从主内存中读写(不能越级)
    不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来进行(同级不能相互访问)

    多线程先行发生原则之happens-before

    在JMM中,
    如果一个操作执行的结果需要对另一个操作可见性
    或者代码重排序,那么这两个操作之间必须存在happens-before(先行发生)原则。罗福i的生玄

    原则说明

    如果Java内存模型中所有的有序性都仅靠volatile和synchronized来完成,那么有很多操作都将会变得非常啰嗦,但是我们在编写Java并发代码的时候并没有察觉到这一点。
    我们没有时时、处处、次次,添加volatile和synchronized来完成程序,这是因为Java语言中JMM原则下有一个“先行发生”(Happens-Before)的原则限制和规矩,给你立好了规矩!
    这个原则非常重要:
    它是判断数据是否存在竞争,线程是否安全的非常有用的手段。依赖这个原则,我们可以通过几条简单规则一揽子解决并发环境下两个操作之间是否可能存在冲突的所有问题,而不需要陷入Java内存模型苦涩难懂的底层编译原理之中。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lh2GVTTc-1658366526367)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220720140344181.png)]

    happens-before总原则

    如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
    两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。

    1+2+3=3+2+1

    happens-before之八条

    1.次序规则:

    一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作;

    前一个操作的结果可以被后续的操作获取。
    讲白点就是前面一个操作把变量x赋值为1,那后面一个操作肯定能知道x已经变成了1。

    2.锁定规则:

    一个unLock操作先行发生于后面((这里的“后面”是指时间上的先后))对同一个锁的lock操作;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ql1avejW-1658366526367)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220720140500018.png)]

    3.volatile变量规则:

    对一个volatile变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的,这里的“后面”同样是指时间上的先后。

    4.传递规则:

    如果操作A先行发生于操作B,而操作B又先行发生于操作c,则可以得出操作A先行发生于操作C;

    5.线程启动规则(Thread Start Rule):

    Thread对象的start()方法先行发生于此线程的每一个动作

    6.线程中断规则(Thread lnterruption Rule):

    对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;

    可以通过Thread.interrupted()检测到是否发生中断

    也就是说你要先调用interrupt()方法设置过中断标志位,我才能检测到中断发送

    7.线程终止规则(Thread Termination Rule):

    线程中的所有操作都先行发生于对此线程的终止检
    测,我们呵以通过isAlive()等手段检测线程是否已经终止执行。

    8.对象终结规则(Finalizer Rule):

    一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始

    对象没有完成初始化之前,是不能调用finalized()方法的

    多线程先行发生原则之happens-beforez总结

    在Java语言里面,Happens-Before 的语义本质上是一种可见性
    A Happens-Before B意味着A发生过的事情对B来说是可见的,无论A事件和B事件是否发生在同一个线程里.
    JMM的设计分为两部分:
    一部分是面向我们程序员提供的,也就是happens-before规则,它通俗易懂的向我们程序员阐述了一个强内存模型,我们只要理解happens-before规则,就可以编写并发安全的程序了。
    另一部分是针对JVM实现的,为了尽可能少的对编译器和处理器做约束从而提高性能,JMM在不影响程序执行结果的前提下对其不做要求,即允许优化重排序。我们只需要关注前者就好了,也就是理解happens-before规则即可,其它繁杂的内容有JMM规范结合操作系统给我们搞定,我们只写好代码即可。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vLgDHufP-1658366526368)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220720141539060.png)]

    我们就这段简单的代码一次分析happens-before的规则(规则5、6、7、8可以忽略,因为他们和这段代码毫无关系)

    1由于两个方法是由不同的线程调用,不在同一个线程中,所以肯定不满足程序次序规则;
    2两个方法都没有使用锁,所以不满足锁定规则;
    3变量不是用volatile修饰的,所以volatile变量规则不满足;4传递规则肯定不满足;|

    所以我们无法通过happens-before原则推导出线程A happens-before线程B,虽然可以确认在时间上线程A优先于线程B指定,但就是无法确认线程B获得的结果是什么,所以这段代码不是线程安全的。那么怎么修复这段代码呢?

    把getter/setter方法都定义为synchronized方法

    把value定义为volatile变量,由于setter方法对value的修改不依赖value的原值,满足volatile关键字使用场景

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ezjFcnqn-1658366526368)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220720141925496.png)]

    /*
    *使用:把value定义为volatile变量,由于setter方法对value的修改不依赖vaLue的原值,满足volatile关键字使用场景
    *理由:利用volatile保证读取操作的可见性;利用synchronized保证复合操作的原子性结合使用锁和volatile变量来减少同步的开销*/
    private volatile int value = 0;
    public int getValue()l
    {
    return value;//利用voLatile保证读取操作的可见性
    public synchronized int setValue()
    {
    return ++value;[/利用synchronized保证复合操作的原子性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    volatile与JMM

    被volatile修饰变量的特点

    有序(禁止指令重排) 可见

    内存语义

    当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。

    当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,重新回到主内存中读取最新共享变量

    所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取。

    为什么可以保证有序和可见

    内存屏障

    内存屏障

    可见

    写完后立即刷新回主内存并及时发出通知,大家可以去主内存拿最新版,前面的修改对后面所有线程可见

    有序

    重排序

    重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段,有时候会改变程序语句的先后顺序不存在数据依赖关系:可以重排序;
    存在数据依赖关系禁止重排序
    但重排后的指令绝对不能改变原有的串行语义!这点在并发设计中必须要重点考虑!

    是什么

    内存屏障(也称内存栅栏,屏障指令等,是一类同步屏障指令。是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作),避免代码重排序。内存屏障其实就是一种JVM指令,Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatile实现了Java内存模型中的可见性和有序性(禁重排),但volatile无法保证原子性。
    内存屏障之前的所有写操作都要回写到主内存,
    内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果(实现了可见性)。

    写屏障(Store Memory Barrier):告诉处理器在写屏障之前将所有存储在缓存(store bufferes)中的数据同步到主内存。也就是说当看到Store屏障指令,就必须把该指令之前所有写入指令执行完毕才能继续往下执行。
    读屏障(Load Memory Barrier):处理器在读屏障之后的读操作,都在读屏障之后执行。也就是说在Load屏障指令之后就能够保证后面的读取数据指令一定能够读取到最新的数据。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zEafvtrT-1658366526369)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220721085112677.png)]

    因此重排序时,不允许把内存屏障之后的指令重排序到内存屏障之前。一句话:对一个volatile变量的写,先行发生于任意后续对这个
    volatile变量的读,也叫写后读。

    内存屏障分类

    读屏障

    在读指令之前插入读屏障,让工作内存或CPU高速缓存当中的缓存数据失效,重新回到主内存中获取最新数据

    写屏障

    在写指令之后插入写屏障,强制把写缓冲区的数据刷回到主内存中

    细分

    屏障类型指令示例说明
    LoadLoadLoad1; LoadLoad; Load2保证load1的读取操作在load2及后续读取操作之前执行
    StoreStoreStore1; StoreStore; Store2在store2及其后的写操作执行前,保证store1的写操作已刷新到主内存
    LoadStoreLoad1; LoadStore; Store2在stroe2及其后的写操作执行前,保证load1的读操作已读取结束
    StoreLoadStore1; StoreLoad; Load2保证store1的写操作已刷新到主内存之后,load2及其后的读操作才能执行

    内存屏障插入策略

    什么叫保证有序性?

    禁重排 -> 通过内存屏障禁重排

    1重排序有可能影响程序的执行和实现T因此,我们有时候希望告诉JVM你别“自作聪明”给我重排序,我这里不需要排序,听主人的。
    2对于编译器的重排序,JMM会根据重排序的规则,禁止特定类型的编译器重排序。
    3对于处理器的重排序,Java编译器在生成指令序列的适当位置,插入内存屏障指令,来禁止特定类型的处理器排序。
    
    • 1
    • 2
    • 3

    happens-before 之volatile变量规则

    第一个操作第二个操作:普通读写第二个操作: volatile读第二个操作: volatile写
    普通读写可以重排可以重排不可以重排
    volatile读不可以重排不可以重排不可以重排
    volatile写可以重排不可以重排不可以重排

    当第一个操作为volatile读时,不论第二个操作是什么,都不能重排序。这个操作保证了volatile读之后的操作不会被重排到volatile读之前。

    当第二个操作为volatle写时,不论第一个操作是什么,都不能重排序。这个操作保证了volatile写之前的操作不会被重排到volatle写之后。

    当第一个操作为volatile写时,第二个操作为volatile读时,不能重排。

    JMM就将内存屏障插入策略分为4种规则

    读屏障

    在每个volatile读操作的后面插入一个LoadLoad屏障

    在每个volatile读操作的后面插入一个LoadStore 屏障

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xhwitEqV-1658366526369)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220721091754023.png)]

    在每个volatile 读操作的后面插入一个LoadLoad屏障禁止处理器把上面的volatile读与下面的普通读重排序。
    在每个volatile读操作的后面插入一个LoadStore屏障禁止处理器把上面的volatile读与下面的普通写重排序。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YH1lBC46-1658366526370)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220721091650528.png)]

    写屏障

    在每个volatile 写操作的前面插入一个StoreStore屏障

    在每个volatie写操作的后面插入一个StoreLoad屏障

    在每个volatile 写操作的前面插入一个 StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作都已经刷新到主内存中。
    在每个volatile 写操作的后面插入一个 StoreLoad 屏障作用是避免volatile写与后面可能有的volatile读/写操作重排序

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YjDGcjwz-1658366526370)(C:\Users\a1764\AppData\Roaming\Typora\typora-user-images\image-20220721091902059.png)]

  • 相关阅读:
    Day47 代码随想录打卡|二叉树篇---最大二叉树
    微信小程序剪切图片的功能
    《FFmpeg Basics》中文版-03-比特率/帧率/文件大小
    《SpringCloud Alibaba》实战
    关于 Elasticsearch 内存占用及分配
    C语言入门,用什么编译器比较好?
    随便写一写
    Rust 构建开源 Pingora 框架可以与nginx媲美
    手把手教你编写性能测试用例
    vue3:16、Pinia的基本语法
  • 原文地址:https://blog.csdn.net/csvbdd/article/details/125905292