读写锁应用背景
package com.unsafe;
import java.util.HashMap;
import java.util.Map;
public class ReadWriteLockPro {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 6; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp);
},String.valueOf(i)).start();
}
for (int i = 1; i <= 6; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
public void put(String key ,Object value){
System.out.println(Thread.currentThread().getName()+"进来写"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok");
}
public void get(String key){
System.out.println(Thread.currentThread().getName()+"进来读"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读完了");
}
}
解决方案: 使用ReadWriteLock
package com.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockPro {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
for (int i = 1; i <= 6; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp);
},String.valueOf(i)).start();
}
for (int i = 1; i <= 6; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCacheLock{
private volatile Map<String,Object> map=new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// Lock lock = new ReentrantLock(); 类比原来的锁
// 更细粒度的锁
// 写锁 保证在任意一个时刻只有1个线程在写
public void put(String key ,Object value){
readWriteLock.writeLock().lock();//加锁
try {
System.out.println(Thread.currentThread().getName()+"进来写"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 解锁
readWriteLock.writeLock().unlock();
}
}
// 读锁 允许多个线程同时读
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"进来读"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读完了");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
public void put(String key ,Object value){
System.out.println(Thread.currentThread().getName()+"进来写"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok");
}
public void get(String key){
System.out.println(Thread.currentThread().getName()+"进来读"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读完了");
}
}
保证了写操作(也就是put()方法)的原子性
独占锁(写锁):一次只能被1个线程占有
共享锁(读锁): 多个线程可以同时占有
阻塞队列
首先它是一个队列,先入先出
在队列满的时候添加元素或者队列空的时候取出元素,此时会阻塞
什么情况下会用到:
- 多线程并发处理
- 线程池
四组API
方法n | 方法一 | 方法二 | 方法三 | 方法四 |
---|---|---|---|---|
方式 | 抛出异常 | 有返回值,不抛异常 | 阻塞等待 | 超时等待 |
添加 | add | offer | put | offer(.,.) |
移除 | remove | poll | take | poll(…) |
队首元素 | element | peek | ---- | — |
方式一:会抛出异常
package com.unsafe;
import org.junit.jupiter.api.Test;
import java.util.concurrent.ArrayBlockingQueue;
public class BlockQueuePro {
/*
* 1.抛出异常的队列
* IllegalStateException: Queue full
* java.util.NoSuchElementException
* */
@Test
public void test1(){
// 参数为队列的大小
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
// arrayBlockingQueue.add("d");
System.out.println("++++++++++++++++++++++++++");
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
}
}
System.out.println(“头”+arrayBlockingQueue.element());
得到头部元素
方式二:有返回值,不抛出异常
/*
* 2.有返回值,不抛出异常
* */
@Test
public void test2(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
// System.out.println(arrayBlockingQueue.offer("d"));
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
}
System.out.println(arrayBlockingQueue.peek()+"头部元素");
获取头部元素
方法三:队满队空则等,一直阻塞,没有返回值
// 等待 阻塞(一直阻塞) 无返回值
@Test
public void test3() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
// arrayBlockingQueue.put("d");
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
}
方法四:有返回值,可以设置超时时间 ,不抛异常,等待规定时间就自动结束
// 有返回值,还可设置超时时间 不抛异常
@Test
public void test4() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
arrayBlockingQueue.offer("a",1, TimeUnit.SECONDS);
arrayBlockingQueue.offer("b",1, TimeUnit.SECONDS);
arrayBlockingQueue.offer("c",1, TimeUnit.SECONDS);
System.out.println("---------------");
System.out.println(arrayBlockingQueue.offer("d", 3, TimeUnit.SECONDS));
System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));
System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));
System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));
System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));
}
SynchronousQueue 同步队列用法详解
可以理解为容量为1 的BlockingQueue的方法三的情形:
进去一个元素之后必须取出这个元素才能添加,不然会死等
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue();
new Thread(()->{
try {
System.out.println("放入了a");
synchronousQueue.put("a");
System.out.println("放入了b");
synchronousQueue.put("b");
System.out.println("放入了c");
synchronousQueue.put("c");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"取出ok");
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"取出ok");
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"取出ok");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}