JUC笔记(作用)

JUC笔记(作用)

二、理解

volatile

volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。

  1. 保证可见性

    1. 线程A操作完后,会将工作内存的值刷新到主存中
  2. 不保证原子性

    1. 多个线程操作时会导致值覆盖,使得不能保持原子性问题
    2. 使用AtomicInteger保持原子性
  3. 禁止指令重排

    什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
    源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
    处理器在进行指令重排的时候,考虑:数据之间的依赖性!

     指令重排:程序运行时的样子不是你所理想型的样子,在不影响程序结果的前提下,指令执行的顺序可以进行调换
       * 初始化参数:a、b、x、y都为0
       * 指令重排前的样子:
       * 线程A:x = a;b = 1* 线程B:y = b;a = 2* 结果:x = 0;y = 0* 指令重排后的样子:
       * 线程A:b = 1;x = a;
       * 线程B:a = 2;y = b;
       * 结果:x = 2;y = 1

CAS机制

CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问

  1. CAS是比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就
    一直循环!
  2. Java的CAS底层是使用Unsafe类操作内存地址值
  3. 缺点:
    1. 循环会耗时
    2. 一次性只能保证一个共享变量的原子性
    3. ABA问题:A值被另一个线程进行了操作形成了新的值

原子引用:解决ABA问题,引用原子引用。乐观锁的思想

  1. 对操作的对象进行版本编号
  2. 每次线程操作时,进行版本号对比
    1. 成功:进行操作
    2. 失败:不进行操作

阻塞队列

阻塞队列,可以理解成生产者消费者模式,是一个支持两个附加操作的队列。

  • 当队列为满的时候,插入元素的线程被阻塞,直达队列不满。
  • 当队列为空的时候,获取元素的线程被阻塞,直到队列不空。
  • ArrayBlockingQueue:由数组结构组成的有界阻塞队列,按照先进先出原则,要求设定初始大小。
  • LinkedBlockingQueue:由链表结构组成的有界,按照先进先出原则,可以不设定初始大小,但是默认值大小为Integer,MAX_VALUE的阻塞队列。
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列。默认情况下,按照自然顺序,要么实现compareTo()方法,指
    定构造参数Comparator。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列,支持延时获取元素的阻塞队列,元素必须要实现Delayed接口。适用场景:实现自己的缓存系统,订单到期,限时支付等等。
  • SynchronousQueue:一个不存储元素的阻塞队列,没有容量,每一个put操作都要等待一个take操作,所以会产生生产者和消费者能力不匹
    配的问题。
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列。transfer(),必须要消费者消费了以后方法才会返回,tryTransfer()
    无论消费者是否接收,方法都立即返回。
  • LinkedBlockingDeque:由链表结构组成的双向阻塞队列。可以从队列的头和尾插入和移除元素,实现工作密取,方法名带了First对头部操作,带了last从尾部操作,另外:add=addLast;
    remove=removeFirst; take=takeFirst。

BlockingQueue方法有四种API形式

抛出异常返回值阻塞超时
插入add(Object obj)offer(Objject obj)put(Object obj)offer(Object obj,int time,unit)
移除remove()pull()take()pull(time,unit)
检查element()peek()不可用不可用

线程池

三大方法七大参数四种拒绝策略

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程

好处:

  1. 降低资源的消耗
  2. 提高响应的速度
  3. 对线程方便管理

三大方法

// 单个线程
ExecutorService threadPool01 = Executors.newSingleThreadExecutor();
// 创建一个固定的线程池的大小
ExecutorService threadPool02 = Executors.newFixedThreadPool(5);
// 可伸缩的,遇强则强,遇弱则弱
ExecutorService threadPool03 = Executors.newCachedThreadPool();

阿里手册说明使用ThreadPoolExecutor来创建线程、规避资源耗尽的风险

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(5, 5,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

七大方法:

// 本质ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime, // 超时了没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂:创建线程的,一般不用动
RejectedExecutionHandler handle // 拒绝策略) {
                          
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

四种拒绝策略就是对应阻塞队列的四种API

Lock锁

Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition

通过Condition实现等待通知机制

Condition类可以实现多路通知功能,就是Lock对象可以创建多个Condition,将线程对象注册在指定的Condition中,就可以实现有通知性地进行线程通知,在调度线程上更加灵活了。

Synchronized 和 Lock 区别

  1. Synchronized 内置的Java关键字, Lock 是一个Java类
  2. Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
  4. Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
    去;
  5. Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
    自己设置);
  6. Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!

各种锁的理解

公平锁:从字面来看 "公平"两个字体现,线程获取锁的顺序是按照线程加锁的顺序来分配的,也就是队列中常说的先进先出的顺序。

非公平锁:就是以一种抢占的方式来获取锁,是随机获得锁,这样靠抢占的方式肯定会出现有的线程会获不到锁,所以说叫非公平的。

可重入锁:线程里面锁可以进行重复加锁操作。注意点lock锁每次都是一把新锁

自旋锁: 它是为实现保护共享资源而提出一种锁机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IYFs1KOX-1612187380220)(13_Java中JUC.assets/1612186894377.png)]

死锁:线程间抢夺对方的锁,导致出现死锁问题

死锁测试:线程A锁抢夺线程B的锁,线程B的锁抢夺线程A的锁

解决方案:

  1. 使用 jps -l 定位进程号
  2. 使用 jstack 进程号 找到死锁问题

ForkJoin

大任务可以拆成小任务,小任务还可以继续拆成更小的任务,最后把任务的结果汇总合并,得到最终结果,这种模型就是Fork/Join模型

java.util.concurrent包中的以下四个类是学习fork/join框架的核心:

  • ForkJoinPool
  • ForkJoinTask
  • RecursiveAction
  • RecursiveTask

ForkJoinPool类的一个实例表示一个线程池。 ForkJoinTask类的一个实例表示一个任务。

ForkJoinTask类是一个抽象类。它有两个具体的子类:RecursiveAction和RecursiveTask。

Java 8添加了一个称为CountedCompleter的ForkJoinTask类的抽象子类。

该框架支持两种类型的任务:不产生结果的任务和产生结果的任务。

RecursiveAction类的实例表示不产生结果的任务。 RecursiveTask类的实例表示产生结果的任务。

CountedCompleter任务可能产生结果,也可能不产生结果。

这两个类,RecursiveAction和RecursiveTask,提供了一个抽象的compute()方法。

我们应该继承这些类之一,并为compute()方法提供一个实现。

  • 常用的辅助类

  1. CountDownLatch(减法计数器)

允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。

CountDownLatch一个有用的属性是,它不要求调用countDown线程等待计数到达零之前继续,它只是阻止任何线程通过await ,直到所有线程可以通过

  1. CyclicBarrier(加法计数器)

允许一组线程全部等待彼此达到共同屏障点的同步辅助。 循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。 屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。

等待所有的线程执行完成才会释放

  1. Semaphore(信号量)

一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。 每个release()添加许可证,潜在地释放阻塞获取方。 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行

一种限流操作:资源溢满时,等待。资源释放时,使用

  • 高并发下的集合类

在高并发情况下,Java之前的集合类都是不安全。会出现java.util.ConcurrentModificationException 并发修改异常!

  1. List
    1. 使用原生线程安全的类:new Vector()
    2. 使用工具类创建线程安全:Collections.synchronizedList(new ArrayList<>());
    3. 使用JUC下的线程安全类:CopyOnWriteArrayList
      1. CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;
      2. 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
      3. 在写入的时候避免覆盖,造成数据问题!
  2. Set
    1. 使用工具类创建线程安全:Collections.synchronizedSet(new HashSet<>());
    2. 使用JUC下的线程安全类:CopyOnWriteArraySet
  3. Map
    1. 使用工具类创建线程安全:Collections.synchronizedMap(new HashMap<>())
    2. 使用JUC下的线程安全类:ConcurrentHashMap
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值