赞
踩
网课链接 https://www.bilibili.com/video/BV1Kw411Z7dF?from=search&seid=17866093614757364189&spm_id_from=333.337.0.0
相关笔记链接 https://blog.csdn.net/weixin_47872288/article/details/119453092
代码地址 https://gitee.com/userwhz/juc-ssg
java.util.concurrent工具包的简称 进程 线程 程序执行的最小单元 线程的状态 NEW 新建 RUNNABLE 准备就绪 BLOCKED 阻塞 WAITING 不见不散 TIMED_WAITING 过时不候 TERMINATED 终结 wait和sleep的区别 wait是Thread的静态方法,wait是Object的方法,任何对象实例都能调用 sleep不会释放锁,也不需要占用锁,wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中) 都可以被interrupted方法中断 并发 不是同一时刻 并行 同一时刻 管程 Monitor(监视器) 保证同一时间,只有一个线程 用户线程 自定义线程 守护线程 比如垃圾回收 没有用户线程,守护线程也会结束
synchronizd 同步锁 卖票案例 多线程编程步骤一 创建资源类,创建属性和操作方法 创建多线程调用资源类的方法 创建线程的多种方式 继承Thread类 少用 实现Runnable接口 使用Callable接口 使用线程池 Lock接口 java.util.concurrent.locks 需要手动上锁和释放锁 实现类 ReentrantLock 可重入锁 可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁 卖票案例 Lock和synchronized去呗 Lock是接口,synchronized是关键字,是内置的语言实现 synchronized发生异常时,会自动释放锁,因此不会死锁。Lock发生异常时,没有手动释放锁的话,很容易造成死锁现象
多线程编程步骤二 资源类 判断 干活 通知 synchronized实现线程间通信 wait notifyAll 虚假唤醒问题 循环判断while解决 Lock实现线程间通信 condition await signalAll
让线程按顺序执行特定次数
通过标志位实现
Arraylist线程不安全演示 java.util.ConcurrentModificationException 并发修改异常 解决方案 1)Vector 2)Collections 3)CopyOnWriteArraylist 读的时候支持并发读 写的时候独立写 HashSet线程不安全 不重复 无序 CopyOnWriteArraySet HashMap线程不安全 ConcurrentHashMap
synchronized八种锁问题 synchronized在方法名上锁的是对象 static synchronized 锁定的是class static synchronized 和 synchronized 锁的不一样 公平锁 ReentrantLock lock = new ReentrantLock (true) 公平锁 默认false 非公平 非公平锁 优点 效率高 缺点 造成线程饿死 可重入锁 又叫做递归锁 synchronized 隐式 lock 显式 都是可重入锁 死锁 死锁出现一定会出现以下四个条件,但是出现以下四个条件不一定死锁 1)互斥: 在一个时间一个资源只能由一个进程使用 2)持有并等待: 进程保持至少一个资源正在等待获取其他进程持有的额外资源 3)无抢占: 一个资源只能被进程资源释放,进程已经完成了它的任务之后 4)循环等待: 存在等待进程集合{P0,P1,...,Pn},P0正在等待P1所占用的资源,P1正在等待P2占用的资源...Pn-1在等待Pn的资源,Pn正在等 待P0所占用的资源
创建线程方法 继承Thread类 实现Runnable接口 通过Callable接口 线程池方式 Runnbale当线程终止时,无法使线程返回结果 Callable接口 和 Runnable 是否有返回值 call 无法计算结果时,抛出异常 Runnable接口有实现类FutureTask
减少计数 CounutDownLatch await 变成0后输出 countDown -1 循环栅栏 CyclicBarrier await 信号灯 Semaphore acquire release
悲观锁 乐观锁 表锁 整张表上锁 行锁 只对一条记录上锁 可能会发生死锁 读锁 共享锁 可能会发生死锁 写锁 排他锁 可能会发生死锁 readWriteLock.writeLock().lock(); readWriteLock.writeLock().unlock(); readWriteLock.readLock().lock(); readWriteLock.readLock().unlock(); 读写锁 一个资源可以被多个读线程访问,或者可以被一个写线程访问,但是不可以同时存在读线程和写线程。 锁饥饿问题,一直读没有写 读的时候不能进行写操作 锁降级 将写入锁降级为读锁 获取写锁 获取读锁 释放写锁 释放读锁 读锁不能升级为写锁 ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock(); ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
阻塞队列 不需要关注什么时候唤醒线程,什么时候阻塞线程 分类 1)ArrayBlockingQueue 基于数组的阻塞队列 由数组结构组成的有界阻塞队列 2)LinkedBlockingQueue 基于链表的阻塞队列 由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列 3)DelayQueue 使用优先级队列实现的延迟无界阻塞队列 DelayQueue 中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue 是一个没有大小限制的队列,因此往 队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞 4)PriorityBlockingQueue 基于优先级的阻塞队列 支持优先级排序的无界阻塞队列 不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者 5.SynchronousQueue 一种无缓冲的等待队列 相对于有缓冲的 BlockingQueue 来说,少了一个中间经销商的环节(缓冲区) 不存储元素的阻塞队列,也即单个元素的队列 声明一个 SynchronousQueue 有两种不同的方式,它们之间有着不太一样的行为。 公平模式和非公平模式的区别: • 公平模式:SynchronousQueue 会采用公平锁,并配合一个 FIFO 队列来阻塞 多余的生产者和消费者,从而体系整体的公平策略; • 非公平模式(SynchronousQueue 默认):SynchronousQueue 采用非公平锁,同时配合一个 LIFO 队列来管理多余的生产者和消费者 而后一种模式,如果生产者和消费者的处理速度有差距,则很容易出现饥渴的情况,即可能有某些生产者或者是消费者的数据永远都得不 到处理 6.LinkedTransferQueue 由链表结构组成的无界阻塞 TransferQueue 队列 由链表组成的无界阻塞队列 预占模式。意思就是消费者线程取元素时,如果队列不为空,则直接取走数据,若队列为空,生成一个节点(节点元素为 null)入队,消 费者线程被等待在这个节点上,生产者线程入队时发现有一个元素为 null 的节点,生产者线程就不入队了,直接就将元素填充到该节 点,并唤醒该节点等待的线程,被唤醒的消费者线程取走元素,从调用的方法返回 7.LinkedBlockingDeque 由链表结构组成的双向阻塞队列 阻塞有两种情况 插入元素时: 如果当前队列已满将会进入阻塞状态,一直等到队列有空的位置时再该元素插入,该操作可以通过设置超时参数,超时后返 回 false 表示操作失败,也可以不设置超时参数一直阻塞,中断后抛出 InterruptedException异常 读取元素时: 如果当前队列为空会阻塞住直到队列不为空然后返回元素,同样可以通过设置超时参数 方法 抛出异常 特殊值 组塞 超时 插入 add offer put offer 移除 remove poll take poll 检查 element peek 不可用 不可用
特点 - 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的销耗。 - 提高响应速度: 当任务到达时,任务可以不需要等待线程创建就能立即执行。 - 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 使用方法 Executors.newFixedThreadPool(int)一池N线程 ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5个窗口 Executors.newSingleThreadExecutor()一池一线程 ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //一个窗口 Executors.newCachedThreadPool()一池可扩容根据需求创建线程 ExecutorService threadPool3 = Executors.newCachedThreadPool(); 线程池7个参数含义 int corePoolSize 常驻线程数量(核心) int maximumPoolSize 最大线程数量 long keepAliveTime,TimeUnit unit 线程存活时间 时间单位 BlockingQueue<Runnable> workQueue 阻塞队列(排队的线程放入) ThreadFactory threadFactory 线程工厂,用于创建线程 RejectedExecutionHandler handler 拒绝测试(线程满了) 拒绝策略 AbortPolicy(默认) 直接抛出RejectedExecutionException异常阻止系统正常运行 callerRunsPolicy “调用者运行“—种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任 务的流量。 DiscardOldestPolicy 抛弃队列中等待最久的任务,然后把当前任务加人队列中尝试再次提交当前任务。 DiscardPolicy 该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的—种策略。 具体工作流程 在执行创建对象的时候不会创建线程 创建线程的时候execute()才会创建 先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数 和阻塞队列之和后,会执行拒绝策略。
将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果 该算法相当于递归,且是二分查找思路 ForkJoinTask:我们要使用 Fork/Join 框架,首先需要创建一个 ForkJoin 任务。该类提供了在任务中执行 fork 和 join 的机制。通常情况下 我们不需要直接集成 ForkJoinTask 类,只需要继承它的子类,Fork/Join 框架提供了两个子类: RecursiveAction:用于没有返回结果的任务 RecursiveTask:用于有返回结果的任务 ForkJoinPool:ForkJoinTask 需要通过 ForkJoinPool 来执行 RecursiveTask: 继承后可以实现递归(自己调自己)调用的任务
有返回值的异步任务 supplyAsync 没有返回值的异步任务 runAsync ``` public class CompletableFutureDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{ System.out.println(Thread.currentThread().getName()); }); completableFuture1.get(); CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{ System.out.println(Thread.currentThread().getName()); int i = 1 / 0; return 1024; }); completableFuture2.whenComplete((t,u)->{ System.out.println(t);//返回值 System.out.println(u);//异常信息 }).get(); } } ```
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。