| 序号 | 锁名称 | 应用 |
|---|---|---|
| 1 | 乐观锁 | CAS |
| 2 | 悲观锁 | synchronized、vector、hashtable |
| 3 | 自旋锁 CAS | |
| 4 | 可重入锁 | synchronized、Reentrantlock、Lock |
| 5 | 读写锁 | ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet |
| 6 | 公平锁 | Reentrantlock(true) |
| 7 | 非公平锁 | synchronized、reentrantlock(false) |
| 8 | 共享锁 | ReentrantReadWriteLock中读锁 |
| 9 | 独占锁 | synchronized、vector、hashtable、ReentrantReadWriteLock中写锁 |
| 10 | 重量级锁 | synchronized |
| 11 | 轻量级锁 | 锁优化技术 |
| 12 | 偏向锁 | 锁优化技术 |
| 13 | 分段锁 | concurrentHashMap |
| 14 | 互斥锁 synchronized | |
| 15 | 同步锁 | synchronized |
| 16 | 死锁 | 相互请求对方的资源 |
| 17 | 锁粗化 | 锁优化技术 |
| 18 | 锁消除 | 锁优化技术 |
乐观锁
乐观锁是一种乐观思想,假定当前环境是读多写少,遇到并发写的概率比较低,读数据时认为别的线程不会正在进行修改(所以没有上锁)。写数据时,判断当前 与期望值是否相同,如果相同则进行更新(更新期间加锁,保证是原子性的)。
Java中的乐观锁: CAS,比较并替换,比较当前值(主内存中的值),与预期值(当前线程中的值,主内存中值的一份拷贝)是否一样,一样则更新,否则继续进行CAS操作。
如上图所示,可以同时进行读操作,读的时候其他线程不能进行写操作。
悲观锁
悲观锁是一种悲观思想,即认为写多读少,遇到并发写的可能性高,每次去拿数据的时候都认为其他线程会修改,所以每次读写数据都会认为其他线程会修改,所以每次读写数据时都会上锁。其他线程想要读写这个数据时,会被这个线程block,直到这个线程释放锁然后其他线程获取到锁。
Java中的悲观锁: synchronized修饰的方法和方法块、ReentrantLock。
如上图所示,只能有一个线程进行读操作或者写操作,其他线程的读写操作均不能进行。
查询当前镜像
npm get registry
全局安装yarn
npm install -g yarn
看一下yarn版本,运行以下命令
yarn -v
改为淘宝镜像
yarn config set registry https://registry.npm.taobao.org
最新地址
npm config set registry https://registry.npmmirror.com
安装项目依赖
yarn install
运行项目
yarn run dev
打包
yarn run build
Deque是一个双端队列接口,继承自Queue接口,Deque的实现类是LinkedList、ArrayDeque、LinkedBlockingDeque,其中LinkedList是最常用的。
Deque有三种用途:
普通队列(一端进另一端出):
Queue queue = new LinkedList()或Deque deque = new LinkedList()
双端队列(两端都可进出)
Deque deque = new LinkedList()
堆栈
Deque deque = new LinkedList()
注意:Java堆栈Stack类已经过时,Java官方推荐使用Deque替代Stack使用。Deque堆栈操作方法:push()、pop()、peek()。
Deque是一个线性collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。
此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。插入操作的后一种形式是专为使用有容量限制的 Deque 实现设计的;在大多数实现中,插入操作不能失败。
下表总结了上述 12 种方法:
| 第一个元素 (头部) | 最后一个元素 (尾部) | ||
|---|---|---|---|
| 抛出异常 | 特殊值 | 抛出异常 | 特殊值 |
| 插入 | addFirst(e) | offerFirst(e) | addLast(e) |
| 删除 | removeFirst() | pollFirst() | removeLast() |
| 检查 | getFirst() | peekFirst() | getLast() |
Deque接口扩展(继承)了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue 接口继承的方法完全等效于 Deque 方法,如下表所示:
| Queue方法 | 等效Deque方法 |
|---|---|
| add(e) | addLast(e) |
| offer(e) | offerLast(e) |
| remove() | removeFirst() |
| poll() | pollFirst() |
| element() | getFirst() |
| peek() | peekFirst() |
双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法,如下表所示:
| 堆栈方法 | 等效Deque方法 |
|---|---|
| push(e) | addFirst(e) |
| pop() | removeFirst() |
| peek() | peekFirst() |
| 方法 | 说明 |
|---|---|
| add方法 | 往队列尾部加入元素 |
| addFirst方法 | 往队列首部加入元素 |
| addLast方法 | 往队列尾部加入元素 |
| contains方法 | 判断队列中是否包含某个元素 |
| descendingIterator方法 | 以相反的顺序返回deque |
| element方法 | 检索但不删除返回队列的头部 |
| getFirst方法 | 与element相同,检索但不删除返回第一个元素,具体不知道为什么会有重复的方法,应该是版本更新兼容吧,就语义来说这个好一点 |
| getLast方法 | 检索并返回最后一个元素 |
| iterator方法 | 以正确的顺序返回此元素的迭代器 |
| offer方法 | 将指定元素插入该元素的尾部,返回true或者false,与add的区别是add当没有可用空间时会抛异常,而offer返回false |
| offerFirst方法 | 在双端队列头部插入元素 |
| offerLast方法 | 在双端队列尾部插入元素,同offer一样,为了Queue和Deque兼容 |
| peek()方法和peekFirst/peekLast方法 | 返回但不删除双端队列的首元素/尾元素 |
| poll方法和pollFirst/pollLast方法 | poll检索并删除队列的首元素,pollFirst也是,pollLast检索并删除队列的尾元素 |
| pop方法 | pop从这个deque表示的堆栈中弹出一个元素,与poll不同的是pop当为空时会报异常,而poll会返回null |
| push方法 | 在此deque的首部添加元素 |
| remove方法以及removeFirst/removeLast方法 | remove和removeFirst检索并删除队列中第一个元素,removeLast检索并删除队列中最后一个元素(同poll相同,就不演示了) |
| size方法 | 返回此的确中的元素数 |
@RequestParam:接收来自RequestHeader中,即请求头。通常用于GET请求,例如:
java@GetMapping("/hello")
public String hello(@RequestParam(name = "id") Long id){
System.out.println("hello " + id);
return "hello message";
}
POST请求 由于@RequestParam是用来处理 Content-Type 为 application/x-www-form-urlencoded 编码的内容的,所以在postman中,要选择body的类型为 x-www-form-urlencoded,这样在headers中就自动变为了 Content-Type : application/x-www-form-urlencoded 编码格式。如下所示:
java@PostMapping("/save")
public String hello(@RequestParam(name = "id") Long id,
@RequestParam("name") String name,
@RequestParam("password") String password){
System.out.println(user);
return "hello message";
}
注解@RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非 Content-Type: application/x-www-form-urlencoded编码格式的数据,比如:application/json、application/xml等类型的数据。
就application/json类型的数据而言,使用注解@RequestBody可以将body里面所有的json数据传到后端,后端再进行解析。
java@PostMapping("/saveBatch")
public String saveBatch(@RequestBody @Validated List<User> list){
list.forEach(System.out::println);
return "saveBatch success";
}
java
@ApiOperation(value = "导出编目质控历史")
@PostMapping("/exportQcCatalogRecord")
public void exportQcCatalogRecordList(HttpServletResponse response, @RequestBody QcCatalogRecordRequest request){
request.setPage(-1);
request.setRows(-1);
IPage<QcCatalogV> ipage=iRecCatalogQcService.getQcCatalogRecordList(request);
List<QcCatalogV> list=ipage.getRecords();
try {
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode("编目质控历史" + ".xls", "utf-8"));
ExportParams exportParams=new ExportParams();
// 生成workbook 并导出
Workbook workbook = ExcelExportUtil.exportBigExcel(exportParams, QcCatalogV.class, list);
ExcelExportUtil.closeExportBigExcel();
workbook.write(response.getOutputStream());
workbook.close();
} catch (IOException e) {
e.printStackTrace();
throw new BusinessException(ResultCodeEnum.ERR_0x1000.getCode(),e.getMessage());
}
}