JavaEE——JUC

JavaEE传送门

JavaEE

JavaEE——常见的锁策略

JavaEE——CAS



JUC

JUC 全称 java.util.concurrent

1. Callable 接口

类似于 Runnable. Runnable 描述的任务, 不带返回值. Callable 描述的任务是带返回值的.

示例: 创建线程, 通过线程来计算 1 + 2 + 3 + . . . + 1000

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //使用 callable 定义一个任务
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i = 1; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };

        //存在的意义, 就是为了我们能够获取到结果(获取到结果的凭证)
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        //创建线程来执行上述任务
        //Thread 的构造方法, 不能直接传 callable , 还需要一个中间的类
        Thread t = new Thread(futureTask);
        t.start();

        //获取线程的计算结果
        //get 方法会阻塞, 直到 call 方法计算完毕, get 才会返回
        System.out.println(futureTask.get());
    }
}

总结: 线程创建的方式

  1. 继承 Thread
  2. 实现 Runnable
  3. 使用 lambda
  4. 使用线程池
  5. 使用 Callable

2. ReentrantLock

可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全.

核心用法:

  1. lock( ): 加锁, 如果获取不到锁就死等
  2. tryLock(超时时间): 加锁, 如果获取不到锁, 等待一定时间之后就放弃加锁
  3. unlock( ): 解锁

小缺点

import java.util.concurrent.locks.ReentrantLock;

public class Demo1 {
    public static void main(String[] args) {
        ReentrantLock locker = new ReentrantLock();

        //加锁
        locker.lock();

        //解锁
        locker.unlock();
    }
}

在上述代码中, 如果在 locker.lock();locker.unlock(); 之间, 出现了 return, 或者有异常, 就可能导致 unlock 执行不了.

解决方案: 使用try finally

import java.util.concurrent.locks.ReentrantLock;

public class Demo1 {
    public static void main(String[] args) {
        ReentrantLock locker = new ReentrantLock();

        try{
            //加锁
            locker.lock();
        }finally {
            //解锁
            locker.unlock();
        }
    }
}

优势

ReentrantLock 有一些特定的功能, 是 sychronized 做不到的.

  1. tryLock 试试看能不能加上锁. 成功了, 就加锁成功. 试失败了, 就放弃. 并且还可以指定加锁的等待超时时间.

  2. ReentrantLock 可以实现 公平锁. (默认是非公平的), 构造的时候,传入一个简单的参数, 就成了公平锁.

    ReentrantLock locker = new ReentrantLock(true);
    
  3. sychornized 是搭配 wait/ notify 实现等待通知机制的, 唤醒操作是一个随机的过程.

    ReentrantLock 是搭配 Condition 类实现的, 可以指定唤醒哪一个等待的线程.


3. 原子类

原子类的底层, 是基于 CAS 实现的, Java 里面已经封装好了, 可以直接来使用.

使用原子类, 最常见的场景就是多线程计数, 写了个服务器, 服务器一共有多少并发量, 就可以通过这样的原子变量来累加.

示例

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger count = new AtomicInteger(0);

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                // 相当于 count++
                count.getAndIncrement();
                /*// 相当于 ++count
                count.incrementAndGet();
                // 相当于 count--
                count.getAndDecrement();
                // 相当于 count--
                count.decrementAndGet();*/
            }

        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                // 相当于 count++
                count.getAndIncrement();
            }

        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(count);
    }
}

运行结果展示


4. 线程池

JavaEE——No.2 多线程案例(内含线程池)

JavaEE——线程小知识(线程和线程池的好处)


5. 信号量 Semaphore

信号量的基本操作是两个:

  1. P 操作, 申请一个资源
  2. V操作, 释放一个资源

信号量本身是一个计数器, 表示可用资源的个数.

  • P 操作申请一个资源, 可用资源就 -1
  • V 操作释放一个资源, 可用资源就 +1
  • 当计数为 0 的时候, 继续 P 操作, 就会产生阻塞等待, 阻塞等待到其他线程 V 操作了为止.

信号量可以视为是一个更广义的锁, 锁就是一个特殊的信号量 (可用资源只有 1 的信号量)

我们把互斥锁也看做是计数为 1 的信号量.(取值只有 1 和 0, 也叫做 二元信号量)

Java 标注库提供了 Semaphore , 这个类, 其实就是把 操作系统 提供的信号量封装了一下.

示例

import java.util.concurrent.Semaphore;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        // 构造的时候需要指定初始值, 计数器的初始值, 表示有几个可用资源
        Semaphore semaphore = new Semaphore(4);
        // P 操作, 申请资源, 计数器 - 1
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        semaphore.acquire();
        System.out.println("P 操作");
        // V 操作, 申请资源, 计数器 + 1
        semaphore.release();
        System.out.println("V 操作");
    }
}

上述代码中, 我们在只有 4 个可用资源的前提下, 进行了 5次 P 操作, 会发生什么呢?

我们发现, 进行了 4次 P操作 之后, 就开始阻塞等待, 这个阻塞会一直阻塞下去, 直到有人进行 V操作.

# 注意 #

当需求中, 有多个可用资源的时候, 要记得使用信号量.


6. CountDownLatch

同时等待 N 个任务执行结束.

好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才结束比赛.

使用 CountDownLatch 就是类似的效果, 使用时先设置有几个选手, 选手撞线的时候, 就调用一下 CountDownLatch 方法, 当撞线次数达到了选手的个数, 就认为比赛结束了.

示例

import java.util.concurrent.CountDownLatch;

public class CountDownLatch1 {
    public static void main(String[] args) throws InterruptedException {
        // 有 10 个选手参加了比赛
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for(int i = 0; i < 10; i++){
            //创建 10 个线程来执行一批任务
            Thread t = new Thread(() -> {
                System.out.println("选手出发! " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("选手到达! "  + Thread.currentThread().getName());
                //撞线
                countDownLatch.countDown();
            });
            t.start();
        }

        //await 是进行阻塞等待, 会等到所有选手都撞线之后, 才结束阻塞
        countDownLatch.await();
        System.out.println("比赛结束! ");
    }
}

运行结果展示


🌷(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))🌷

以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!
在这里插入图片描述
加粗样式

这里是Gujiu吖!!感谢你看到这里🌬
祝今天的你也
开心满怀,笑容常在。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值