JUC并发编程,线程池(介绍、使用、参数),原子类(Atomic-线程安全),锁机制(Lock),信号量工具类(Tools),并发集合类

一、JUC(java.util.concurrent)并发编程

Jdk5开始出现

JUC:让开发者在多线程编程中更加简单、方便

通过JDK内置了一些类、接口、关键字,补充完善了JDK对于并发编程支持的“短板”

提供了并发编程、线程安全相关的类和接口,方便并发编程

1. JUC中的类和接口

线程创建中使用的 Callable接口 与 Future接口都属于JUC中的内容

查看JUC中的类与接口

  1. 找到java.util.concurrent包
  2. 选中concurrent点击右键,选择Diagrams
  3. 选择show Diagram

2. 主要包含的功能

Executor:线程池

Atomic:原子操作类

Lock:锁

Tools:信号量工具类

二、线程池

1. 为什么使用线程池:

        线程的创建和销毁,会消耗一定的系统资源。如果频繁的新建和销毁线程,可能会大量的消耗系统资源。

        如何在需要频繁的创建、销毁线程的场景下保证系统响应时间更快,减少资源消耗? --- 使用线程池。

2. 什么是线程池:

        内容的一块空间。这块空间中存放了一些实例化好的线程对象。

        当代码中需要使用线程时直接从线程池获取。当代码中线程执行结束或需要销毁时,把线程重新放入回到线程池,而不是让线程处于死亡状态。

3. 线程池特点:

3.1 优点:

  1. 降低系统资源的消耗。通过重用线程对象,降低新建和销毁线程产生的资源消耗
  2. 提高系统的响应速度。直接获取内存中的线程对象,减少新建线程的时间。
  3. 提供线程的可管理性。可以限制线程池中的线程数量,避免无限制创建或cpu资源耗尽等问题

3.2 缺点:

        默认情况下,无论需要还是不需要线程,线程池中都会有线程对象,会占用一定的内存空间。

4.Executor-JUC中的线程池

4.1Executor介绍:

Executor(执行器)线程池中的顶级接口,有个execute()方法,参数为Runnable接口类型

JUC并发编程,线程池(介绍、使用、参数),原子类(Atomic-线程安全),锁机制(Lock),信号量工具类(Tools),并发集合类_第1张图片

5. 线程池参数

        corePoolSize:核心线程数

        maximumPooSize:最大线程数(核心线程数+临时线程数)

        keepAliveTime:空闲时间

                线程的空闲时间超过指定的时间,线程消亡,直到线程池中的个数为核心线程数

        TimeUnit unit:空闲时间单位  枚举类型

                纳秒、微秒、毫秒、秒、分钟、小时、天

        BlockingQueue<Runnable> workQueue:阻塞队列

                队列:底层实现 数组、链表;先进先出 单向

                队列任务为空:阻塞获取任务

                队列任务满时:阻塞添加任务

        ThreadFactory threadFactory:线程工厂---创建线程对象

        RejectedExecutionHandler handler:拒绝策略   ThreadPoolExecutor中的静态内部类

  1. AbortPolicy: 丢弃新任务,抛出异常,提示线程池已满(默认)。
  2. DisCardPolicy: 丢弃任务,不抛出异常。
  3. DisCardOldSetPolicy: 将消息队列中最先进入队列的任务替换为当前新进来的任务。
  4. CallerRunsPolicy: 由调用该任务的线程处理, 线程池不参与, 只要线程池未关闭,该任务一直在调用者线程中。

三、Atomic-原子类(线程安全)

        原子性:操作过程中不允许其他线程干扰, 可以理解为数据操作是整体,整体只有成功或失败,不允许出现部分成功部分失败。

        与synchronized同步锁的原子性有些差异:多个线程操作同一资源,只能一个线程持有锁操作资源,其他线程需要等待,等待该线程执行完,其他线程再抢占资源(cpu分配)

例如:

        num++在多线程下执行是不安全的, num++可以分解为

        1. 读取num的值(从主内存拷贝)

        2. 对num+1(在工作内存中, 操作拷贝的副本)

        3. 把结果赋值给num(同步到主内存)

        所以说num++是不具备原子性的。

        如果希望num++具备原子性,可以把num++的三个步骤看做一个不可拆分的整体。

JUC并发编程,线程池(介绍、使用、参数),原子类(Atomic-线程安全),锁机制(Lock),信号量工具类(Tools),并发集合类_第2张图片

1. AtomicInteger

AtomicInteger a = new AtomicInteger();

提供的方法:

getAndIncrement(): a++,先获取值再自增

incrementAndGet(): ++a,先自增再获取值

getAndDecrement(): a--,先获取值再自减

decrementAndGet(): --a,先自减再获取值

getAndAdd(num): 先获取值,再加指定值

addAndGet(num): 先加指定值,再获取值

set(num): 修改值  a = num

getAndSet(): 先获取值,在修改为指定值

get(): 获取值

底层实现:

原子类AtomicInteger底层使用的是volatile和cas

四、Lock-JUC锁机制

1. locks包介绍

Java.util.concurrent.locks:JUC中对锁机制支持提供的包

JUC并发编程,线程池(介绍、使用、参数),原子类(Atomic-线程安全),锁机制(Lock),信号量工具类(Tools),并发集合类_第3张图片

2. JUC锁机制(AQS)

        AQS全名AbstractQueuedSynchronizer,是并发容器JUC(java.util.concurrent)下locks包内的一个类。

        它实现了一个FIFO(FirstIn、FisrtOut先进先出)的队列。底层实现的数据结构是一个双向链表

JUC并发编程,线程池(介绍、使用、参数),原子类(Atomic-线程安全),锁机制(Lock),信号量工具类(Tools),并发集合类_第4张图片

工作原理:

        AQS的核心思想为如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是使用队列实现的锁,即将暂时获取不到锁的线程加入到队列中

        AQS使用一个int state成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。

        AQS使用CAS对该同步状态进行原子操作实现对其值的修改,当state大于0的时候表示锁被占用,如果state等于0时表示没有占用锁。

3. 锁机制介绍

JUC中锁的底层使用的就是AQS

  1. ReentrantLock: Lock接口的实现类, 可重入锁。相当于synchronized同步锁
  2. ReentrantReadWriteLock:ReadWriteLock接口的实现类。类中包含两个静态内部类,ReadLock读锁、WriteLock写锁。
  3. Condition:是一个接口,都是通过lock.newCondition()实例化。属于wait和notify的替代品。提供了await()、signal()、singnalAll()与之对应
  4. LockSupport:和Thread中suspend()和resume()相似

五、Tools-JUC信号量工具类

Tools是JUC中的工具类, 其中包含:CountDownLatch、CyclicBarrier、Semaphore

1. CountDownLatch倒计时锁存器

        在开发中经常遇到在主线程中开启多个线程去并行执行任务,并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。之前是使用join() | 主线程休眠实现的,但是不够灵活,某些场合和还无法实现,所以开发了CountDownLatch这个类。底层基于AQS。

        CountDown是计数递减的意思,Latch是门闩(shuan)的意思。内部维持一个递减的计数器。可以理解为初始有n个Latch,等Latch数量递减到0的时候,结束阻塞, 执行后续操作。

2. CyclicBarrier回环屏障

CountDownLatch优化了join()在解决多个线程同步时的能力,但CountDownLatch的计数器是一次性的。计数递减为0之后,再调用countDown()、await()将不起作用。为了满足计数器可以重置的目的,JDK推出了CyclicBarrier类。

await()方法表示当前线程执行时计数器值不为0则等待。如果计数器为0则继续执行。每次await()之后计算器会减少一次。当减少到0下次await从初始值重新递减。

3. Semaphore信号量

CountDownLatch和CyclicBarrier的计数器递减的,而Semaphore的计数器是可加可减的,并可指定计数器的初始值,并且不需要事先确定同步线程的个数,等到需要同步的地方指定个数即可。且Semaphore也具有回环重置的功能,这一点和CyclicBarrier很像。底层也是基于AQS(AbstractQueuedSynchronizer)。

六、并发集合类

并发集合类:主要是提供线程安全的集合

比如:

        1. ArrayList对应的并发类是CopyOnWriteArrayList

        2. HashSet对应的并发类是 CopyOnWriteArraySet -- 底层数组实现

        3. HashMap对应的并发类是ConcurrentHashMap

        这些类的方法API和之前学习的ArrayList、HashSet、HashMap的API是相同的,所以重在实现原理上, 而不是API的使用上

你可能感兴趣的:(java,知识点,java,jvm,JUC并发编程)