JUC中的常见类

简介: JUC中的常见类

一.ReentrantLock

ReentrantLock是juc包中提供的一种锁,用来保证线程安全,其相对于synchronized存在一些不同点

上锁方式:ReentrantLock是手动进行上锁和解锁的,这就意味着相对于synchronized,reentrantlock更容易发生死锁现象,(当上锁之后,在解锁之前发生异常,这时候解锁无法进行,其他线程想要获取锁,这时候会发生死锁现象),为了避免死锁现象的产生,我们必须将reentrantLock上锁到解锁的代码放入trycatch finally代码块中,保证解锁的执行

ReentrantLock reentrantLock1 = new ReentrantLock();

       try {

           reentrantLock1.lock();

       }catch (Exception e){

           e.printStackTrace();

       }finally {

           reentrantLock1.unlock();

       }

需要注意的是ReentrantLock()中有一个tryLock()方法,它有一定的等待时间,如果在等待时间内获取不到锁,就放弃索取该锁,去执行其他任务

2.相对于synchronized,其支持公平锁:

ReentrantLock reentrantLock = new ReentrantLock(true);

9a5d2aac734727e02ed3ebd78620d909.png

3.阻塞和唤醒的方式:我们知道,synchronized使用的是Object类中的wait()、notify()和notifyall()的方法进行阻塞和唤醒,而ReentrantLock进行阻塞和解锁的方式如下:

//创建对象

       ReentrantLock reentrantLock = new ReentrantLock(true);

       //需要手动进行上锁和解锁

       try {

           reentrantLock.lock();

           //创建condition对象

           Condition condition = reentrantLock.newCondition();

           condition.await();

           condition.signal();

           condition.signalAll();

reentrantLock对象利用newCondition()方法,创建阻塞对象(在底层创建一个条件对象),使用其await()、signal()、和signalAll()方法进行阻塞和唤醒,但是synchronized在底层维护一个阻塞队列和就绪队列,但是ReentrantLock中的每一个condition对象都会维护一个阻塞队列,不同线程会根据条件的不同进入不同的阻塞队列,最终维护一个就绪队列和多个阻塞队列

442cb110bc08e929b9b1f9340eba7d0c.png

4.ReentrantLock支持读写锁:我们可以利用ReentrantReadWriteLock类创建读锁和写锁,其中读锁是共享锁,写锁是独占锁

ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

           //获取读锁

           ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

           //获取写锁

           ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

二.原子类

原子类是利用CAS来来保证线程安全的实现方式,其性能要比加锁操作高很多


image.png

我们写以下代码来体会其作用:

public class TestAtomic {

   public static void main(String[] args) throws InterruptedException {

       AtomicInteger atomicInteger = new AtomicInteger();

        Thread t1 =new Thread(()->{

                   for (int i=0;i<50000  ;++i){

                       atomicInteger.getAndIncrement();

                   }

                });

        t1.start();

         Thread t2 =new Thread(()->{

             for (int i = 0; i < 50000; i++) {

                 atomicInteger.getAndIncrement();

             }

                 });

         t2.start();

         t1.join();

         t2.join();

       System.out.println(atomicInteger);

   }

}

image.png

其内部机制:利用CAS+自旋保证原子性

image.png

我们没有使用synchronized进行上锁,却没有产生原子性问题,所以它的作用就像它的名字一样:保持原子性,我们去说明其中的几个方法:

image.png

三.JUC中的工具类

3.1 Semaphore(信号量)

semaphore用来记录可用资源的个数,在本质上说是一个计数器

可以把信号量想象成是停车场的展示牌: 当前有车位 100 个. 表示有 100 个可用资源.

当有车开进去的时候, 就相当于申请一个可用资源, 可用车位就 -1 (这个称为信号量的 P 操作)

当有车开出来的时候, 就相当于释放一个可用资源, 可用车位就 +1 (这个称为信号量的 V 操作)

如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源.

Semaphore 的 PV 操作中的加减计数器操作都是原子的, 可以在多线程环境下直接使用.


import java.util.concurrent.Semaphore;

import java.util.concurrent.TimeUnit;

 

/**

* @author tongchen

* @create 2023-02-08 16:06

*/

public class SemaphoreTest {

   public static void main(String[] args) {

       //创建信号量

       Semaphore semaphore = new Semaphore(5);

       //创建任务

    Runnable runnable=new Runnable() {

           @Override

           public void run() {

               //消耗资源

               try {

                   semaphore.acquire();

                   System.out.println( Thread.currentThread().getName()+"获取到资源了");

                   //执行任务

                   System.out.println(Thread.currentThread().getName()+"在执行任务");

                   //释放资源

                   TimeUnit.SECONDS.sleep(2);

                   System.out.println(Thread.currentThread().getName()+"释放资源了");

                   semaphore.release();

               } catch (InterruptedException e) {

                   throw new RuntimeException(e);

               }

 

           }

       };

    //通过for循环不断创建线程

       for (int i = 0; i <10 ; i++) {

            Thread thread=new Thread(runnable,i+"");

            thread.start();

       }

 

 

   }

}

3.2 CountDownLatch

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

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

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.TimeUnit;

 

/**

* @author tongchen

* @create 2023-02-08 16:21

*/

public class CountDownLatchTest {

   public static void main(String[] args) throws InterruptedException {

       //创建10个任务

       CountDownLatch countDownLatch = new CountDownLatch(10);

       Runnable runnable=new Runnable() {

           @Override

           public void run() {

               //准备就绪

               System.out.println(Thread.currentThread().getName()+"准备就绪");

               //开始运行

               System.out.println(Thread.currentThread().getName()+"开始运行");

               try {

                   TimeUnit.SECONDS.sleep(2);

                   System.out.println(Thread.currentThread().getName()+"运行结束");

                   countDownLatch.countDown();

 

               } catch (InterruptedException e) {

                   throw new RuntimeException(e);

               }

           }

       };

       for (int i = 0; i < 10; i++) {

           Thread thread=new Thread(runnable,"任务"+i);

           thread.start();

       }

       //等待任务全部执行结束

       countDownLatch.await();

       System.out.println("任务全部执行结束了......");

   }

}

a47b1939aac5359e867fb4763f4b7cb9.png

7c06f5b47f394ad61ee81e2fcde408f2.png

3.3线程安全的集合类

当我们在多线程中使用集合类,同样会产生线程安全问题:

import java.util.ArrayList;

import java.util.Arrays;

 

/**

* @author tongchen

* @create 2023-02-08 16:34

*/

public class ArraylistWithMultithreading {

   public static void main(String[] args) {

       //创建集合

       ArrayList<Integer> arrayList = new ArrayList<>();

       //创建任务并加入多线程

       for (int i = 0; i < 10; i++) {

           int x=i;

           Thread  thread=new Thread(()->{

                   arrayList.add(x);

           });

           thread.start();

           System.out.println(arrayList);

       }

       System.out.println("-----------------------");

       System.out.println(arrayList);

   }

}

6402f89350ac988d2e01f85f53220afe.png

我们如何解决集合类中的线程安全问题呢?

  1. 手动加锁,使用synchronized包裹代码块或者使用reentrantlock手动加锁释放锁(一定要注意线程问题存在很多,我们在不同线程中进行读和写的操作都要记得加锁)

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Collections;

import java.util.List;

import java.util.concurrent.CopyOnWriteArrayList;

import java.util.concurrent.locks.ReentrantLock;

 

/**

* @author tongchen

* @create 2023-02-08 16:34

*/

public class ArraylistWithMultithreading {

  static Object loker=new Object();

   public static void main(String[] args) {

       //创建集合

       ArrayList<Integer> arrayList = new ArrayList<>();

       List<Integer> list = Collections.synchronizedList(arrayList);

       ReentrantLock reentrantLock = new ReentrantLock();

       CopyOnWriteArrayList<Integer> integers = new CopyOnWriteArrayList<>(arrayList);

       //创建任务并加入多线程

       for (int i = 0; i < 11; i++) {

 

 

               int x=i;

               Thread  thread=new Thread(()->{

 

                   reentrantLock.lock();

                       arrayList.add(x);

                   reentrantLock.unlock();

 

 

               });

           thread.start();

           reentrantLock.lock();

           System.out.println(arrayList);

           reentrantLock.unlock();

 

       }

       System.out.println("-----------------------");

   }

}

  1. 使用vector等线程安全的集合
  2. 使用工具类:使用Conllections类,将集合包裹

 

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Collections;

import java.util.List;

import java.util.concurrent.CopyOnWriteArrayList;

 

/**

* @author tongchen

* @create 2023-02-08 16:34

*/

public class ArraylistWithMultithreading {

  static Object loker=new Object();

   public static void main(String[] args) {

       //创建集合

       ArrayList<Integer> arrayList = new ArrayList<>();

       List<Integer> list = Collections.synchronizedList(arrayList);

       CopyOnWriteArrayList<Integer> integers = new CopyOnWriteArrayList<>(arrayList);

       //创建任务并加入多线程

       for (int i = 0; i < 11; i++) {

 

 

               int x=i;

               Thread  thread=new Thread(()->{

 

                       list.add(x);

 

 

               });

               thread.start();

 

 

           System.out.println(list);

       }

       System.out.println("-----------------------");

       System.out.println(list);

   }

}

使用CopyOnWriteArraylist

如果是读Arraylist,不需要进行加锁,但是如果是对ArrayList进行写操作时,

此时使用 CopyOnWriteArrayList就是把这个ArrayList'给复制了一份,先修改

副本,修改之后引用再指向副本,保证修改的同时对于读操作是没有任何影响的,读的时候先读旧的版本,不会出现读到一个没修改完的中间状态,适用于多读少写的业务场景

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Collections;

import java.util.List;

import java.util.concurrent.CopyOnWriteArrayList;

 

/**

* @author tongchen

* @create 2023-02-08 16:34

*/

public class ArraylistWithMultithreading {

  static Object loker=new Object();

   public static void main(String[] args) throws InterruptedException {

       //创建集合

       ArrayList<Integer> arrayList = new ArrayList<>();

       List<Integer> list = Collections.synchronizedList(arrayList);

       CopyOnWriteArrayList<Integer> integers = new CopyOnWriteArrayList<>(arrayList);

       //创建任务并加入多线程

       for (int i = 0; i < 5; i++) {

 

 

               int x=i;

               Thread  thread=new Thread(()->{

 

                       integers.add(x);

 

 

               });

               thread.start();

           

 

 

           System.out.println(integers);

       }

       System.out.println("-----------------------");

       System.out.println(integers);

   }

}

相关文章
|
7天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
6天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
318 130
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
18天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1331 8
|
5天前
|
监控 JavaScript Java
基于大模型技术的反欺诈知识问答系统
随着互联网与金融科技发展,网络欺诈频发,构建高效反欺诈平台成为迫切需求。本文基于Java、Vue.js、Spring Boot与MySQL技术,设计实现集欺诈识别、宣传教育、用户互动于一体的反欺诈系统,提升公众防范意识,助力企业合规与用户权益保护。
|
17天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
1412 87
|
6天前
|
人工智能 Java API
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
本文介绍AI大模型的核心概念、分类及开发者学习路径,重点讲解如何选择与接入大模型。项目基于Spring Boot,使用阿里云灵积模型(Qwen-Plus),对比SDK、HTTP、Spring AI和LangChain4j四种接入方式,助力开发者高效构建AI应用。
312 122
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
|
5天前
|
JavaScript Java 大数据
基于JavaWeb的销售管理系统设计系统
本系统基于Java、MySQL、Spring Boot与Vue.js技术,构建高效、可扩展的销售管理平台,实现客户、订单、数据可视化等全流程自动化管理,提升企业运营效率与决策能力。
|
6天前
|
弹性计算 安全 数据安全/隐私保护
2025年阿里云域名备案流程(新手图文详细流程)
本文图文详解阿里云账号注册、服务器租赁、域名购买及备案全流程,涵盖企业实名认证、信息模板创建、域名备案提交与管局审核等关键步骤,助您快速完成网站上线前的准备工作。
253 82
2025年阿里云域名备案流程(新手图文详细流程)