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);

   }

}

相关文章
|
5天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
15天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
9天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
600 215
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
836 61
|
7天前
|
人工智能 移动开发 自然语言处理
2025最新HTML静态网页制作工具推荐:10款免费在线生成器小白也能5分钟上手
晓猛团队精选2025年10款真正免费、无需编程的在线HTML建站工具,涵盖AI生成、拖拽编辑、设计稿转代码等多种类型,均支持浏览器直接使用、快速出图与文件导出,特别适合零基础用户快速搭建个人网站、落地页或企业官网。
1231 157
|
4天前
|
编解码 Linux 数据安全/隐私保护
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
236 138
|
7天前
|
存储 安全 固态存储
四款WIN PE工具,都可以实现U盘安装教程
Windows PE是基于NT内核的轻量系统,用于系统安装、分区管理及故障修复。本文推荐多款PE制作工具,支持U盘启动,兼容UEFI/Legacy模式,具备备份还原、驱动识别等功能,操作简便,适合新旧电脑维护使用。
516 109