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

目录
  • 一、对象锁juc.locks包
  • 二、原子类
  • 三、四个常用工具类
    • 3.1 信号量 Semaphore
    • 3.2 CountDownLatch
  • 总结

一、对象锁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在本篇就不多介绍,读者可以自行查阅一下官方文档进行仔细的学习~如果有问题可以私信博主,别忘了点赞收藏+关注哦!

(0)

相关推荐

  • java并发包JUC诞生及详细内容

    目录 前言 关于JCP和JSR DougLea和他的JSR-166 Lock接口的原型 CountDownLatch的原型 AbstractQueuedSynchronizer抽象类的原型 JSR-166的详细内容 1.请描述拟议的规范: 2.什么是目标Java平台? 3.拟议规范将解决Java社区的哪些需求? 4.为什么现有规范不满足这种需求? 5.请简要介绍基础技术或技术: 6.API规范是否有建议的包名? 7.建议的规范是否与您知道的特定操作系统,CPU或I/O设备有任何依赖关系? 8.当

  • java.util.ConcurrentModificationException 解决方法

     java.util.ConcurrentModificationException 解决方法 在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除. 则使用会报以下异常: Java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) at java.util.HashMap$Key

  • 浅谈java.util.concurrent包中的线程池和消息队列

    1.java并发包介绍 JDK5.0(JDK1.5更名后)以后的版本引入高级并发特性,大多数的特性在java.util.concurrent包中,是专门用于多线程编程的,充分利用了现代多处理器和多核心系统的功能以编写大规模并发应用程序.主要包括原子量.并发集合.同步器.可重入锁,并对线程池的构造提供了强力的支持 2.线程池 java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池 多线程技术主要解决

  • 深入Synchronized和java.util.concurrent.locks.Lock的区别详解

    主要相同点:Lock能完成Synchronized所实现的所有功能.主要不同点:Lock有比Synchronized更精确的线程予以和更好的性能.Synchronized会自动释放锁,但是Lock一定要求程序员手工释放,并且必须在finally从句中释放.synchronized 修饰方法时 表示同一个对象在不同的线程中 表现为同步队列如果实例化不同的对象 那么synchronized就不会出现同步效果了.1.对象的锁 所有对象都自动含有单一的锁. JVM负责跟踪对象被加锁的次数.如果一个对象被

  • java.util.concurrent.ExecutionException 问题解决方法

    java.util.concurrent.ExecutionException错误信息,这里给出解决方案,大家根据具体要求更改. SEVERE: A child container failed during start java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].Standa

  • 出现java.util.ConcurrentModificationException 问题及解决办法

    java.util.ConcurrentModificationException 解决办法 前言: 在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除. 则使用会报以下异常: Java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) at java.util.HashMap$

  • 详解Java删除Map中元素java.util.ConcurrentModificationException”异常解决

    今天在使用map并需要根据某些条件删除map元素时,自然而然想到调用Map中的remove(Object key)函数进行删除,代码如下: //遍历map,如果key<5,那么就删除此元素. Map<Integer, Integer> users = new LinkedHashMap<Integer, Integer>(); for (Map.Entry<Integer,Integer> entry : users.entrySet()){ for (int i

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

    目录 一.对象锁juc.locks包 二.原子类 三.四个常用工具类 3.1 信号量 Semaphore 3.2 CountDownLatch 总结 一.对象锁juc.locks包 在Java中除了synchronized关键字可以实现对象锁之外,java.util.concurrent中的Lock接口也可以实现对象锁. 介绍一下这个lock锁的简要实现: JDK1.0就有的,需要JVM借助操作系统提供的mutex系统原语实现 JDK1.5之后,Java语言自己实现的互斥锁实现,不需要借助操作系

  • Java中JUC 的 Exchange 交换器详情

    目录 前言 基础使用 总结 前言 Exchange(交换器)顾名思义,它是用来实现两个线程间的数据交换的,它诞生于 JDK 1.5, 它有两个核心方法: exchange(V x):等待另一个线程到达此交换点,然后将对象传输给另一个线程,并从另一个线程中得到交换的对象.如果另一个线程未到达此交换点,那么此线程会一直休眠(除非遇了线程中断). exchange(V x, long timeout, TimeUnit unit):等待另一个线程到达此交换点,然后将对象传输给另一个线程,并从另一个线程

  • mybatis源码解读-Java中executor包的语句处理功能

    目录 1.mybatis对多语句类型的支持 2.mybatis的语句处理功能 1.mybatis对多语句类型的支持 在mybatis映射文件中传参数,主要用到#{} 或者 ${}. #{}:表示使用这种符号的变量会以预编译的形式赋值到sql片段中. ${}:表示使用这种符号的变量会以字符串的形式直接插到sql片段中. mybatis中支持三种语句类型,不同语句类型支持的变量符号不同.mybatis的三种类型如下: STATEMENT:这种语句类型中,只会对sql片段进行简单的字符串拼接.只支持使

  • Java中使用正则表达式的一个简单例子及常用正则分享

    import java.util.Scanner; public class regexTest { // 新建类 public static void main(String[] args){ // 主方法 Scanner sc = new Scanner(System.in); // new Scanner类对象 System.out.println("Please Enter Email:"); String email = sc.nextLine(); System.out.p

  • 深入理解Java中包的定义与使用

    目录 包是什么? 包的作用 导入包中的类 自定义包 包的访问权限控制 包是什么? 在开发过程中,会定义很多类,随着类越写越多,难免会出现类重名而发生覆盖的情况,为了在使用它们的时候不让编译器混淆,我们给类加上一个限定 (前缀),把所有java程序保存在各自的目录里面,而该目录就是包,包的本质实际上就是一个文件夹 即:把它们放在不同的包里面,调用时再加上前缀即可:这样好处是对同名的类进行了区分:能精确指出我们需要的哪一个:让同名的类在一个程序中可以共存 在Java中,包是对类.接口等的封装机制的体

  • Java多线程Atomic包操作原子变量与原子类详解

    在阅读这篇文章之前,大家可以先看下<Java多线程atomic包介绍及使用方法>,了解atomic包的相关内容. 一.何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中,是不能被中断的.通常来说,原子指令由硬件提供,供软件来实现原子方法(某个线程进入该方法后,就不会被中断,直到其执行完成) 在x86平台上,CPU提供了在指令执行期间对总线加锁的手段.

  • 一文秒懂Java中的乐观锁 VS 悲观锁

    乐观锁 VS 悲观锁 悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁.写锁.行锁等),当其他线程想要访问数据时,都需要阻塞挂起. 乐观锁:总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改. 乐观锁在Java中通过使用无锁来实现,常用的是CAS,Java中原子类的递增就是通过CAS自旋实现. CAS CAS全称 Compare And Swap(比较与交换),是一种

  • Java中JSON字符串与java对象的互换实例详解

    在开发过程中,经常需要和别的系统交换数据,数据交换的格式有XML.JSON等,JSON作为一个轻量级的数据格式比xml效率要高,XML需要很多的标签,这无疑占据了网络流量,JSON在这方面则做的很好,下面先看下JSON的格式, JSON可以有两种格式,一种是对象格式的,另一种是数组对象, {"name":"JSON","address":"北京市西城区","age":25}//JSON的对象格式的字符串 [

  • Java 中ConcurrentHashMap的实现

    ConcurrentHashMap(简称CHM)是在Java 1.5作为Hashtable的替代选择新引入的,是concurrent包的重要成员.在Java 1.5之前,如果想要实现一个可以在多线程和并发的程序中安全使用的Map,只能在HashTable和synchronized Map中选择,因为HashMap并不是线程安全的.但再引入了CHM之后,我们有了更好的选择.CHM不但是线程安全的,而且比HashTable和synchronizedMap的性能要好.相对于HashTable和sync

随机推荐