JUC的常见类

Callable interfacce

也是一种创建线程的方式
Runnable 能表示一个任务(run方法),返回void
Callable 也能表示一个任务(call方法),返回一个具体的值,类型可以通过泛型参数来指定(object)
如果进行多线程操作,如果你只是关心多线程的执行过程,使用Runnable即可,如果是关心多线程的计算结果,使用Callable更合适
使用Callable不能直接作为Thread的构造方法参数,我们可以借用FutureTask来完成
,运算的结果什么时候能算出来,可以使用FutureTask来帮助我们解决这个问题

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

public class demo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum =0;
                for (int i =0;i<1000;i++){
                    sum +=i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask =new FutureTask<>(callable);
        Thread t1 =new Thread(futureTask);
        t1.start();
        //获取call方法返回的结果,get类似于join,如果call方法没有执行完,就会阻塞等待
        Integer result = futureTask.get();
        System.out.println(result);
    }
}

在这里插入图片描述

ReentrantLock

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

ReentrantLock 的用法:
lock(): 加锁, 如果获取不到锁就死等.
trylock(超时时间): 加锁, 如果获取不到锁, 等待一定的时间之后就放弃加锁.
unlock(): 解锁
虽然synchronized也是一个可重入锁,但是这两者还是又明显的区别的

ReentrantLock的优点

1.提供了一个trylock的方法进行加锁
对于lock操作来说,如果加锁不成功,就会出现阻塞等待(死等).
对于trylock,如果加锁失败,直接返回false/也可以设定等待时间,该方法给锁操作提供了更多的可操作空间
2.有两种模式,可以工作在公平锁的状态下,也可以工作在非公平锁的状态下,构造方法中可以通过参数设定的公平/非公平模式
3.也有一个等待通知机制,搭配condition这样的类来完成,这里的等待要比wait notify功能更强

ReentrantLock的缺点

unlock()解锁方法容易被人忘记,常常需要搭配finally来使用
synchronized的锁对象可以是任意对象
ReentrantLock的锁对象就只能是自己本身,如果多个线程针对不同的ReentrantLock调用lock方法,此时是不会产生锁竞争的

相同点:

synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁

不同点:

用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块;ReentrantLock 只能用于代码块;
获取和释放锁的机制不同:进入synchronized 块自动加锁和执行完后自动释放锁; ReentrantLock 需要显示的手动加锁和释放锁;
锁类型不同:synchronized 是非公平锁; ReentrantLock 默认为非公平锁,也可以手动指定为公平锁;
响应中断不同:synchronized 不能响应中断;ReentrantLock 可以响应中断,可用于解决死锁的问题;
底层实现不同:synchronized 是 JVM 层面通过监视器实现的;ReentrantLock 是基于 AQS 实现的。

原子类

原子类内部用的是 CAS 实现,所以性能要比加锁实现 i++ 高很多。原子类有以下几个

AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicLong
AtomicReference
AtomicStampedReference

以 AtomicInteger 举例,常见方法有

addAndGet(int delta); i += delta;
decrementAndGet(); --i;
getAndDecrement(); i–;
incrementAndGet(); ++i;
getAndIncrement(); i++;
(具体可看CAS详解)

线程池

虽然创建销毁线程比创建销毁进程更轻量, 但是在频繁创建销毁线程的时候还是会比较低效.
线程池就是为了解决这个问题. 如果某个线程不再使用了, 并不是真正把线程释放, 而是放到一个 “池子”
中, 下次如果需要用到线程就直接从池子中取, 不必通过系统来创建了.
(具体可看线程池详解)

信号量Semaphore

在操作系统中,也经常出现,是并发编程的一个重要的概念/组件
准确来说,Semaphore是一个计数器(变量),描述了"可用资源的个数",描述的是,当前这个线程,是否有"临界"资源可以用,所谓的临界资源,是指的多个线程/进程等并发执行的实体可以公共使用到的资源(多个线程修改同一个变量,这个变量就可以被认为是一个临界资源)

示例

停车场景:
停车场的入口,上面会挂这一个显示屏,上面会显示停车场内可用的车位
如果开车进入停车场,申请一个车位(申请了一个可用资源),此时计数器就会+1,称为p操作
如果开车离开停车场,释放了一个车位(释放了一个可用资源),此时计数器就会-1,称为v操作
如果发现车位计数器上面的计数器为0,这个时候,有两种选择:
1.等
2.放弃,寻找下个车位
这里我们选择第一种操作,继续进行p操作,就会阻塞等待,一直等到其他车辆进行了v操作,释放了一个空闲的车位为止,
锁,本质上是一个特殊的信号量(里面的数值,非0即1,二元信号量),信号量要比锁更广义,不仅仅可以描述一个资源,还可以描述N个资源,虽然概念上更广泛,实际开发中,还是锁更多一些(二元信号量的场景是更常见的)

代码示例
创建 Semaphore 示例, 初始化为 4, 表示有 4 个可用资源.
acquire 方法表示申请资源(P操作),
release 方法表示释放资源(V操作)

package Thread;

import java.util.concurrent.Semaphore;

public class demo3 {
    public static void main(String[] args) throws InterruptedException {
        //构造方法中,就可以用来指定计数器的初始值
        Semaphore semaphore = new Semaphore(4);
        semaphore.acquire();//计数器-1
        System.out.println("执行p操作");
        semaphore.acquire();//计数器-1
        System.out.println("执行p操作");
        semaphore.acquire();//计数器-1
        System.out.println("执行p操作");
        semaphore.acquire();//计数器-1
        System.out.println("执行p操作");
        semaphore.acquire();//计数器-1
        System.out.println("执行p操作");
    }
}

在这里插入图片描述

CountDownLatch

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

好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才能公布成绩

构造 CountDownLatch 实例, 初始化 10 表示有 10 个任务需要完成.
每个任务执行完毕, 都调用 latch.countDown() . 在 CountDownLatch 内部的计数器同时自减.
主线程中使用 latch.await(); 阻塞等待所有任务执行完毕. 相当于计数器为 0 了.

package Thread;

import java.util.concurrent.CountDownLatch;

public class demo1 {
    public static void main(String[] args) throws InterruptedException {
    //相当于创建一个线程任务为10的计数器
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0;i<10;i++){
        //防止因为变量捕获的机制使得无法正常访问i变量
            int id =i;
            Thread t = new Thread(()->{
                System.out.println("线程"+id+"开始工作");
                try {
                    //使用sleep来代指某些耗时操作
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程"+id+"结束工作");
                //每次调用countDown就相当于使得计数器减一
                countDownLatch.countDown();
            });
            t.start();
        }
        //如果计数器没有减到0,就会进入阻塞等待
        countDownLatch.await();
        System.out.println("多个线程的所有任务都执行完毕");
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值