一个人内耗,说明他活在过去;一个人焦虑,说明他活在未来。只有当一个人平静时,他才活在现在。
日常
1、起床
2、健身
3、LeetCode刷了1题
- 单词拆分
- 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
- 该问题使用回溯遍历所有的子集会超时,故使用动态规划
- 定义dp数组并确定下标含义:
**dp[i]
表示前i个字符能否被拆分** - 确定递推公式:
dp[i]=遍历所有单词,判断后面部分是否等于单词且前面是否可以拆分
,如果当后面部分与某个单词匹配,且减去后前面部分仍然可拆分,则说明当前可拆分,并求出最小拆分数目 - 初始化
dp[0]=0
,表示0个字符时不可拆分 - 确认遍历顺序:此时是完全背包,因为每个单词都可以放入多次,且求得是排列,因为单词的顺序是有要求的,否则组合字符串不同;故使用一维数组从前向后遍历(每个元素可以放入多次),且先遍历背包再遍历物品
- 求组合时,先遍历物品再遍历背包;求排列时,先遍历背包再遍历物品
- 一定要判断是求组合还是排列
4、复盘
不复盘等于白学!!!
学习和感想
JUC并发编程
1. JUC读写锁
- 悲观锁和乐观锁
- 悲观锁就是操作时一定先进行上锁,等操作结束后再释放锁,就是假设自己操作时别人一定会更新,故操作前一定会加锁;可以避免各种并发问题,但不支持并发操作,效率很低
- 乐观锁就是操作时不进行上锁,当结束操作时先判断数据是否被更改,如果没有更改则更新,否则操作失败;乐观锁支持并发,但有时会操作失败
- 乐观锁操作前不会加锁,但最后会判断数据是否更新,如果更新了则操作失败,即为每个数据添加一个版本号,操作后判断版本号是否未变,如果没变则操作成功,否则更新失败
- 表锁和行锁
- 就是对数据库中的一张表进行加锁,当操作数据库中表的数据时,对整张表进行加锁,此时别的就无法访问该表
- 而行锁时只对数据库某张表的某行数据进行加锁
- 表锁不会发生死锁,但行锁会发生死锁,因为两个相互依赖的数据行可能会被同时加锁,此时就会出现因为竞争资源而造成的相互等待现象
- 读锁和写锁
- 读锁是共享锁,可以由多个线程同时进行读操作
- 写锁是独占锁,每次只允许一个线程执行写操作
- 读锁是共享锁,写锁是独占锁;且读写锁都会发生死锁
- 读锁和写锁都会发生死锁,且写锁可以降级为读锁,因为写锁时可以申请读锁,而读锁时不可以申请写锁,故写锁可以降级为读锁,但读锁不可以升级为写锁
- 案例实现:JUC类中的ReentryentReadWriteLock实现类
- JUC类中的ReentryentReadWriteLock实现类可以返回读锁和写锁对象,读锁是共享锁,写锁是独占锁,且均会出现死锁,且会出现锁饥饿
- JUC工具包中提供了读写锁ReadWriteLock接口,通过该接口的实现类ReentrantReadWriteLock对象可以实现读写锁的操作
- ReadWriteLock维护了一对读写锁,一个用于只读操作,一个用于写入操作,通过readLock()返回读锁,writeLock()返回写锁,并通过lock()和unlock()来加锁和解锁
- 只要没有写,读锁可以被多个线程同时保持,此时别的读线程仍然可以加入读取,此时写入操作会一直等待
- 写入锁是独占的,只要有写入锁,别的写入和读取就无法
- 读锁是共享锁,可以多个线程同时占用;写锁是独占锁,一次只可以有一个线程占用
- 读写锁深入
- 读锁是共享锁,写锁是独占锁,不可以同时存在读写问题
- 就是读者写者问题,此时写锁可能会饥饿,故可以再设置一个信号量,当写锁获得信号量后,读锁就无法再加入进入,从而防止写饥饿
- 为什么要引入读写锁,因为如果使用独占锁,则写问题可以满足,但对于读问题无法实现并发读,效率很低,独占锁无法实现并发读
- 读写锁既可以保证写时独占,也可以保证读时并发共享,从而提高效率,读写锁可以保证写时独占,读时共享,可以保证写时独占,读时共享,从而提高效率
- 但读写锁可能会出现饥饿问题,因为当读锁一直被占用时,就无法进行写操作
- 读写锁读时不可以进行写操作,写时可以进行读操作
- 锁降级
- 读写锁读的时候不可以写,写的时候可以读
- 锁降级就是将写入锁降级为读取锁
- 先获取写锁,再获取读锁(此时读锁未被占用),最后释放写锁
- 读时不可以写,写时可以读:读写锁当获得了读锁后,此时就无法再获取写锁,而获得了写锁后还可以再获得读锁
- 获得了写锁后可以再获得读锁,但获得读锁后无法再获得写锁
2. BlockingQueue阻塞队列
- 概述
- BlockingQueue阻塞队列是线程共享的一个队列,可以读取和存放数据,且会自动实现阻塞和唤醒,当队列为空时,此时读取就会阻塞,当队列满时,此时存放就会阻塞
- 队列是先进先出FIFO,栈是先进后出FILO
- 阻塞队列是一个共享的队列,多个线程对该队列进行操作
- 多个线程共享一个阻塞队列,线程对该阻塞队列进行存取操作
- 当队列为空时,此时取数据的线程就会被阻塞;当队列为满时,此时存数据的线程就会被阻塞
- 直到可以存取时阻塞的线程就会被唤醒
- 当使用阻塞队列后,此时会自动对线程进行阻塞唤醒操作,而不需要手动阻塞和唤醒
- 架构
- BlockingQueue是JUC工具包提供的一个接口类,内部有很多实现类来实现具体的阻塞队列,JUC中提供了很多类型的BlockingQueue,包括数组实现的队列和链表实现的队列等
- 多个线程共享一个阻塞队列,会自动对线程进行阻塞和唤醒操作,当队列为空时,此时取操作就会阻塞,但队列满时,此时存操作就会阻塞,且当可以存取时会自动唤醒
- 分类
- ArrayBlockingQueue:基于数组实现的阻塞队列,内部维护一个定长的数组
- LinkedBlockingQueue:基于链表实现的阻塞队列:内部的队列由链表实现,可以指定最大容量,默认值为最大值
- DelayQueue:使用优先级队列实现的延迟无界阻塞队列,只有当指定的时间到了才可以获取该元素
- 核心方法