Java中JUC包(java.util.concurrent)下的常用子类

 更新时间:2022年12月20日 14:11:53   作者:Killing Vibe  
相信大家已经对并发机制中出现的很多的常见知识点进行了总结,下面这篇文章主要给大家介绍了关于Java中JUC包(java.util.concurrent)下的常用子类的相关资料,文中通过图文以及示例代码介绍的非常详细,需要的朋友可以参考下

一、对象锁juc.locks包

在Java中除了synchronized关键字可以实现对象锁之外,java.util.concurrent中的Lock接口也可以实现对象锁。

介绍一下这个lock锁的简要实现:

  • JDK1.0就有的,需要JVM借助操作系统提供的mutex系统原语实现
  • JDK1.5之后,Java语言自己实现的互斥锁实现,不需要借助操作系统的monitor机制

注:使用Lock接口需要显式的进行加锁和解锁操作。

我们可以使用Lock接口的实现子类ReentrantLock来进行加锁解锁:

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

ReentrantLock 的用法:

  • lock()加锁,获取锁失败的线程进入阻塞状态,直到其他线程释放锁,再次竞争,死等。
  • trylock(超时时间): 加锁, 获取锁失败的线程进入阻塞态,等待一段时间,时间过了若还未获取到锁恢复执行,放弃加锁,执行其他代码
  • unlock()解锁

synchronized和lock的区别:

synchronized 是Java的关键字, 由 JVM 实现,需要依赖操作系统提供的线程互斥原语(mutex),而Lock标准库的类和接口,其中一个最常用的子类( ReentrantLock ,可重入锁),由Java本身实现的,不需要依赖操作系统

synchronized 隐式的加锁和解锁,lock需要显示进行加锁和解锁

synchronized 在获取锁失败的线程时,死等;lock可以使用trylock等待一段时间之后自动放弃加锁,线程恢复执行

synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.

synchronized不支持读写锁,Lock子类ReentrantReadWriteLock支持读写锁。

更强大的唤醒机制. synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是一个随机等待的线程.ReentrantLock搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程

小结:

一般场景synchronized足够用了,需要用超时等待锁,公平锁,读写锁再考虑使用juc.lock

如何选择使用哪个锁?

  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
  • 如果需要使用公平锁, 使用 ReentrantLock.

二、原子类

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

  • AtomicBoolean
  • AtomicInteger
  • AtomicIntegerArray
  • AtomicLong
  • AtomicReference
  • AtomicStampedReference

以 AtomicInteger 举例,常见方法有:

addAndGet(int delta);   i += delta;
decrementAndGet(); --i;
getAndDecrement(); i--;
incrementAndGet(); ++i;
getAndIncrement(); i++;

三、四个常用工具类

juc包下一共有四个常用工具类:

  • 信号量 - Semaphore
  • 计数器 - CountDownLatch
  • 循环栅栏 - CyclicBarrier
  • 两个线程之间的交换器 - Exchanger

3.1 信号量 Semaphore

信号量Semaphore就是一个计数器,表示当前可用资源的个数

关于信号量Semaphore有两个核心操作:

  • P - 申请资源操作
  • V - 释放资源操作

Semaphore 的PV加减操作都是原子性的,再多线程场景下可以直接使用

public static void main(String[] args) {
        // 在构造参数传入可用资源的个数
        // 可用资源为6个
        Semaphore semaphore = new Semaphore(6);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + "准备申请资源");
                    // P操作,每次申请两个资源
                    semaphore.acquire(2);
                    System.out.println(Thread.currentThread().getName() + "获取资源成功");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "释放资源");
                    // V操作,默认释放一个占有的资源
                    semaphore.release(2);
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 20; i++) {
            Thread t = new Thread(runnable,String.valueOf(i + 1));
            t.start();
        }
    }

3.2 CountDownLatch

有点类似于大号的join方法

调用await方法的线程需要等待其他线程将计数器减为0才能继续恢复执行。

public static void main(String[] args) throws InterruptedException {
        // 等待线程需要等待的线程数,必须等这10个子线程全部执行完毕再恢复执行
        CountDownLatch latch = new CountDownLatch(10);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(new Random().nextInt(1000));
                    System.out.println(Thread.currentThread().getName() + "到达终点");
                    // 计数器 - 1
                    latch.countDown();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(runnable,"运动员" + i + 1);
            t.start();
        }
        // main线程就是裁判线程,需要等待所有运动员到底终点再恢复执行
        // 直到所有线程调用countdown方法将计数器减为0继续执行
        latch.await();
        System.out.println("比赛结束~最终获胜的是鹏哥,有请冠军给大家高歌一首~");
    }

总结

至于CyclicBarrier和Exchanger在本篇就不多介绍,读者可以自行查阅一下官方文档进行仔细的学习~如果有问题可以私信博主,别忘了点赞收藏+关注哦!

相关文章

  • SpringMVC打印请求参数和响应数据最优方案

    SpringMVC打印请求参数和响应数据最优方案

    项目中经常需要打印http请求的参数和响应数据,本文给大家讲解如何在SpringMVC打印请求参数和响应数据最优方案,感兴趣的朋友跟随小编一起看看吧
    2023-07-07
  • Java 任务调度框架 Quartz实操

    Java 任务调度框架 Quartz实操

    这篇文章主要介绍了Java 任务调度框架 Quartz,Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。,下面我们来学习一下关于 Quartz更多的详细内容,需要的朋友可以参考一下
    2021-12-12
  • springboot jpa分库分表项目实现过程详解

    springboot jpa分库分表项目实现过程详解

    这篇文章主要介绍了springboot jpa分库分表项目实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Java实现数独小游戏

    Java实现数独小游戏

    这篇文章主要为大家详细介绍了Java实现数独小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-05-05
  • Spring事务的开启原理详解

    Spring事务的开启原理详解

    这篇文章主要介绍了Spring事务的简单实现步骤,帮助大家更好的理解和学习使用spring,感兴趣的朋友可以了解下
    2021-03-03
  • Java中实现在一个方法中调用另一个方法

    Java中实现在一个方法中调用另一个方法

    下面小编就为大家分享一篇Java中实现在一个方法中调用另一个方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • SpringBoot Web依赖教程

    SpringBoot Web依赖教程

    这篇文章主要介绍了SpringBoot Web依赖教程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 如何让Win10实现Java文件的开机自启动

    如何让Win10实现Java文件的开机自启动

    这篇文章主要介绍了如何让Win10实现Java文件的开机自启动,对于一些想要一直运行的Java文件,就会造成每次系统更新之后的重启导致Java文件无法继续运行。,需要的朋友可以参考下
    2019-06-06
  • Java 事务详解及简单应用实例

    Java 事务详解及简单应用实例

    这篇文章主要介绍了Java 事务详解及简单应用实例的相关资料,java事务能够保证数据的完整性和一致性,当然这是书本上的知识,具体如何应用这里举例说明,需要的朋友可以参考下
    2016-12-12
  • java字符串如何只保留数字、字母、中文

    java字符串如何只保留数字、字母、中文

    这篇文章主要介绍了java字符串如何只保留数字、字母、中文问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06

最新评论