JUC(java.util.concurrent) 的常⻅类

目录

Callable接口

ReentrantLock

synchronized 和 ReentrantLock之间的区别

信号量Semaphore

CountDownLatch


JUC(java.util.concurrent) 的常⻅类
Callable接口
Callable 是⼀个 interface . 相当于把线程封装了⼀个 "返回值". ⽅便程序猿借助多线程的⽅式计算结果

Callable接口和Runnable接口并列关系 

返回值call()

泛型参数

代码⽰例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使⽤ Callable 版本
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Demo40 {
    public static void main(String[] args) {
        //此处Callable只是定义了一个“带有返回值”的任务
        //并没有真的在执行,执行还是需要搭配Thread对象
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int ret = 0;
                for (int i = 1; i <= 100; i++) {
                    ret += i;
                }
                return ret;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        System.out.println(futureTask.get());
    }
}

Thread的构造方法,没有提供版本传入Callable对象

Thread本身不提供获取结果的方法

就需要凭FutureTask对象来拿到结果

**代码⽰例: ** 创建线程计算 1 + 2 + 3 + ... + 1000, 不使⽤ Callable 版本
public class Demo41 {
    private static  int total = 0;
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for (int i = 1; i <= 100; i++) {
                    sum += 0;
                }
                total = sum;
            }
        };
        Thread t =new Thread(runnable);
        t.start();
        t.join();
        System.out.println(total);
    }
}
理解 Callable
Callable 和 Runnable 相对, 都是描述⼀个 "任务". Callable 描述的是带有返回值的任务, Runnable
描述的是不带返回值的任务.
Callable 通常需要搭配 FutureTask 来使⽤. FutureTask ⽤来保存 Callable 的返回结果. 因为
Callable 往往是在另⼀个线程中执⾏的, 啥时候执⾏完并不确定.
FutureTask 就可以负责这个等待结果出来的⼯作.
理解 FutureTask
想象去吃⿇辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你⼀张 "⼩票" . 这个⼩票就是
FutureTask. 后⾯我们可以随时凭这张⼩票去查看⾃⼰的这份⿇辣烫做出来了没

ReentrantLock

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

和synchronized是并列关系

更经典风格的锁

import java.util.concurrent.locks.ReentrantLock;

public class Demo42 {
    private static int count = 0;
    public static void main(String[] args) {
        ReentrantLock locker = new ReentrantLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    locker.lock();
                    try{
                        count++;
                    }finally {
                        locker.unlock(); 
                    }
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    locker.lock();
                    try{
                        count++;
                    }finally {
                        locker.unlock();
                    }
                }
            }
        });
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        
    }
}

synchronized 和 ReentrantLock之间的区别

1.synchronized 是关键字(内部实现是jvm内部通过c++实现的)ReentrantLock标准库的类(Java)

2.synchronized通过代码块控制加锁解锁,ReentrantLock需要lock/unlock方法,需要注意unlock不被调用的问题

3.ReentrantLock除了提供lock,unlock之外还提供了一个方法tryLock()

tryLock不会阻塞

加锁成功返回true,失败返回false

调用者判定返回值决定接下来咋做

设置超时时间

等待时间达到超时时间在返回true/false

4.ReentrantLock提供了公平锁的实现

ReentrantLock locker = new ReentrantLock(true);

默认是非公平的

5.ReentrantLock搭配的等待通知机制是Condition类,相比wait,notify来说功能更强大一些


信号量Semaphore
信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器

 能够协调多个进程之间的资源分配

也能协调多个线程之间的资源分配

申请acquire一个资源,计数器就会-1,p操作

释放release一个资源,计数器就会+1,v操作

计数器为0,继续申请就会阻塞等待

import java.util.concurrent.Semaphore;

public class Demo43 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(4);
        semaphore.acquire();
        System.out.println("进行一次p操作");
        semaphore.acquire();
        System.out.println("进行一次p操作");
        semaphore.acquire();
        System.out.println("进行一次p操作");
        semaphore.acquire();
        System.out.println("进行一次p操作");
    }
}

信号量的一个特殊情况,初始值为1 的信号量

取值要么是1要么是0(二元信号量)

等价于“锁”(普通的信号量,就相当于锁的更广泛的推广)

如果是普通的N的信号量,就可以限制同时有多少个线程来执行某个逻辑

import java.util.concurrent.Semaphore;

public class Demo44 {
    private static int count = 0;
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(1);
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    try {
                        semaphore.acquire();
                        count++;
                        semaphore.release();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    try {
                        semaphore.acquire();
                        count++;
                        semaphore.release();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }
            }
        });
        t1.start();
        t2.start();
    }
}

CountDownLatch

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

使用多线程经常把一个大的任务拆分成多个子任务

使用多线程执行这些子任务,从而提高程序的效率

1.构造方法指定参数,描述拆成了多少个任务

2.每个任务执行完毕之后,都调用一次countDown方法

如果指定10个任务,当一共调用了10次countDown说明任务全都完成了

3.主线程中调用await方法,等待所以任务执行完毕

await就会返回/阻塞等待

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo45 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);
        ExecutorService executor = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 10; i++) {
            int id = i;
            executor.submit(()->{
                System.out.println("子任务开始执行"+id);
                try {
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    throw new RuntimeException(e);
                }
                System.out.println("子任务结束执行"+id);
            });

        }
        latch.await();//阻塞等待所有的任务结束
        System.out.println("所有任务执行完毕");
        executor.shutdown();
    }
}

上一篇:C# 删除Word文档中的段落-C# 删除Word中的指定段落


下一篇:SpringSecurity6+OAuth2.0 从入门到熟练使用-简介