1 读写锁(ReadWriteLock)
📌 要点
实现类:ReentrantReadWirteLock
通过读写锁实现更细粒度的控制,当然通过Synchronized和Lock锁也能达到目的,不过他们会在写入和读取操作都给加锁,影响性能;
读写锁在加锁同时,给读取操作进行优化,简单来说性能更高;
读写锁中,读锁是共享锁,多个线程可以同时占有;写锁是独占锁,一次只能被一个线程占有。
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
//写入
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
//读取
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
//自定义缓存
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
//读写锁,更细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存、写入的时候,只希望同时只有一个线程写
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()+"读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
控制台输出:
2 阻塞队列(BlockingQueue)
阻塞队列(BlockingQueue) 是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。
📌 常用实现类
📌 UML相关图
📌 四组API
方式 | 抛出异常 | 不会抛异常,有返回值 | 阻塞等待 | 超时等待 |
添加操作 | add() | offer()供应 | put() | offer(E e, long timeout,TimeUnit unit) |
移除操作 | remove() | poll()获得 | take() | poll(long timeout, TimeUnit unit) |
判断队列首部 | element() | peek()偷看,偷窥 |
📌 代码举例
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
//队列的大小
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
System.out.println(queue.offer("A"));
System.out.println(queue.offer("B"));
System.out.println(queue.offer("C"));
System.out.println(queue.offer("D",2, TimeUnit.SECONDS));
System.out.println("------------------------");
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
}
控制台输出:
3 同步队列(SynchronousQueue)
简单来说,SynchronousQueue是一个没有数据缓冲的阻塞队列,它是实现AbstractQueue接口的。
SynchronousQueue容量为0,生产者线程插入数据(put)必须等待消费者的移除数据(take),反之亦然,也就是说同步队列的插入和移除必须是同步的。
public class SynchronousQueueDemo {
public static void main(String[] args) throws InterruptedException {
SynchronousQueue<String> queue = new SynchronousQueue<>();//同步队列
new Thread(()->{
for (int i = 1; i <= 3; i++) {
try {
queue.put(String.valueOf(i));
System.out.println(Thread.currentThread().getName()+"put "+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 1; i <= 3; i++) {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"take "+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
控制台输出:
📢 可以看到put和take是伴随的,同时执行顺序非固定,说明阻塞队列里边其实不存元素。