• 多线程---JUC


    什么是JUC

    JUC是:java.util.concurrent这个包名的缩写。它里面包含了与并发相关,即与多线程相关的很多东西。我们下面就来介绍这些东西。

    Callable接口

    Callable接口类似与Runnable接口:
    Runnable接口:描述的任务是不带返回值的。
    callable接口:描述的任务是带返回值的,存在的意义就是让我们获取到结果
    让我们通过下面的代码来仔细体会一下不同

        // 使用Runnable来计算 1+2+.....+1000
    
        static class Result{
            public int sum;
            public Object locker = new Object();
        }
    
        public static void main(String[] args) {
            Result result = new Result();
            //创建一个专门的线程来求和
            Thread thread = new Thread(){
                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++){
                        result.sum ++;
    
                    }
                    synchronized (result.locker){
                        result.locker.notify();
                    }
                }
            };
            thread.start();
    
            // 必须得等thread线程执行完了再打印
            synchronized (result.locker) {
    
                if (result.sum == 0){
                    try {
                        result.locker.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                System.out.println(result.sum);
            }
        }
    
    • 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
        // 使用callable来计算1+2+....+1000
        public static void main(String[] args) {
            //使用callable来定义一个任务
            Callable<Integer> callable = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 0; i < 1000; i++){
                        sum++;
                    }
                    return sum;
                }
            };
    
            // 用来接受callable任务作为参数
            FutureTask<Integer> futureTask = new FutureTask<>(callable);
    
            //Thread 里的参数没有callable类型  只有futuretask类型  所以需要创建一个futuretask类型来过渡
            Thread thread = new Thread(futureTask);
            thread.start();
    
            try {
                System.out.println(futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    
    • 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

    ReentrantLock

    ReentrantLock,也是可重入锁。它的出现是为了补充synchronized(可重入锁)无法实现的一些操作。

    它一共有三个核心的方法:

    • tryLock:试试能不能加上锁,试成功了就加上锁;试失败了就放弃。还可以指定加锁的等待超时时间,超过时间则放弃加锁。
    • lock:加锁
    • unlock:解锁

    ReentrantLock VS synchronized

    1. ReentrantLock必须手动调用lock()和unlock()方法,这样如果它们之间出现异常,unlock()方法就有可能调用不到,造成资源浪费。而synchronized则没有这个问题。
           public static void main(String[] args) {
            // 和synchronized相比有三个优势 两个不同
            ReentrantLock reentrantLock = new ReentrantLock();
            try {
                reentrantLock.lock();
            }finally {
                //当在 lock和unlock 之间出现异常时  unlock就无法执行到  需要用到finally来必须执行
                //synchronized不存在这个问题  因为它只要出了代码块就一定会解锁
                reentrantLock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. ReentrantLock是标准库的一个类,底层是基于Java实现的;synchronized是Java关键字,底层是通过JVM实现的(C++实现的)
    2. tryLock可以尝试加锁并且指定加锁的等待超时时间;synchronized会一直死等。在实际开发中,往往不使用死等。
    3. ReentrantLock可以实现公平锁,通过指定构造方法里的一个参数;synchronized是非公平锁。
            ReentrantLock reentrantLock = new ReentrantLock(true);
    
    • 1
    1. ReentrantLock是搭配Condition类实现通知唤醒操作的,唤醒操作可以指定唤醒哪一个线程;synchronized是搭配wait-notify实现通知唤醒操作的,唤醒操作是随机唤醒一个线程。

    原子类

    原子类的底层是基于CAS实现的,使用原子类最常见的场景就是多线程计数。
    CAS操作前面已经非常详细的介绍过,点击此处可以查看浏览

         //原子类  多用于计数
        //count.getAndIncrement   =   count++
        //count.incrementAndGer   =   ++count
        //count.getAndDecrement   =   count--
        //count.decrementAndGer   =   --count
        public static void main(String[] args) {
            AtomicInteger count = new AtomicInteger();
    
            Thread thread = new Thread(() -> {
                for (int i = 0; i < 50000; i++){
                    //相当与count++;
                    count.getAndIncrement();
                }
            });
    
            Thread thread1 = new Thread(() -> {
                for (int i = 0; i < 50000; i++){
                    //相当于count++;
                    count.getAndIncrement();
                }
            });
    
            thread.start();
            thread1.start();
    
            try {
                thread.join();
                thread1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println(count.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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    线程池

    前面已经非常详细的介绍过,点击此处可以查看浏览

    信号量Semaphore

    信号量的基本操作有两个:
    P操作:申请一个资源
    V操作:释放一个资源

    信号量本身是一个计数器,表示可用资源的个数:
    P操作申请一个资源,可用资源的个数就-1
    V操作释放一个资源,可用资源的个数就+1
    当计数为0时,继续进行P操作就会阻塞,直到其他线程执行V操作释放资源

            // 信号量可以看成更广义的锁   锁就是一个信号量  可用资源数只有1
        public static void main(String[] args) throws InterruptedException {
    
            // 需要指定初始值 表示可用资源的个数
            Semaphore semaphore = new Semaphore(4);
    
            semaphore.acquire();
            System.out.println("申请资源");
            semaphore.acquire();
            System.out.println("申请资源");
            semaphore.acquire();
            System.out.println("申请资源");
    
    
            semaphore.release();
            System.out.println("释放资源");
            semaphore.release();
            System.out.println("释放资源");
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    CountDownLatch

    CountDownLatch使用的效果:类似于一个跑步比赛,当最后一个选手到达终点就结束。
    使用CountDownLatch的时候,首先要设置一下有几个选手参赛,每个选手撞线了就调用一下countDown方法,当撞线次数达到选手的个数时,比赛就结束。

    放到程序中理解:
    比如:要下载一个很大的文件,把文件分成多部分分别下载。使用多线程执行下载任务,每个线程下载一部分,当所有的线程都下载完毕,整个线程就下载完毕了。

    	//CountDownLatch
        public static void main(String[] args) {
            // 设置任务的个数
            CountDownLatch downLatch = new CountDownLatch(10);
    
            for (int i = 0; i < 10; i++){
                Thread thread = new Thread(() -> {
                    System.out.println("开始任务" + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("任务结束" + Thread.currentThread().getName());
                    downLatch.countDown();
    
                });
                thread.start();
            }
    
            try {
                downLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("全部任务都结束");
    
        }
    
    • 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
  • 相关阅读:
    MybatisPlus拦截器+注解实现数据权限
    解决mybatis-plus不能俩表联查分页之手动写分页
    卸载Erlang和RabbitMQ
    Educational Codeforces Round 115 (Rated for Div. 2)
    QT中QRadioButton实现分组C++
    pca绘图
    c语言实现玫瑰花
    python爬虫(1)
    Mac电池管理软件 Batteries for Mac v2.2.9直装版
    iOS 之homebrew ruby cocoapods 安装
  • 原文地址:https://blog.csdn.net/weixin_62976968/article/details/134108833