设计模式
单例模式
- 要使用静态方法和静态字段
- 要写个私有的构造方法
private SingleTon(){}
(面试官问我构造器呢,我说不能有构造器,妈的zz) - 外面的类就是get到的类(我有次写成了SingleTonTest而里面的获得对象是Object类型的,吐了,想扇自己)
dcl
public class SingleTon{
private static volatile SingleTon INSTANCE = null;
private SingleTon(){}
public static SingleTon getInstance(){
if(INSTANCE == null){
synchronized(SingleTon.class){
if(INSTANCE == null){
INSTANCE = new SingleTon();
}
}
return INSTANCE;
}
}
}
枚举类
public Enum Singleton{
INSTANCE;
public void method(){
//实现方法。
}
}
静态内部类
- 私有构造方法
很精秒的一点在于每个静态内部类每次只能new一个。
class Singleton {
private Singleton(){}
private static class SingletonTest{
static Singleton INSTANCE = new Singleton();
}
public Singleton get(){
return SingletonTest.INSTANCE;
}
}
生产者和消费者
阻塞队列的实现
public class BlockingQueue {
private List queue = new LinkedList();
private int limit = 10;
public BlockingQueue(int limit){
this.limit = limit;
}
public synchronized void enqueue(Object item)
throws InterruptedException {
while(this.queue.size() == this.limit) {
wait();
}
if(this.queue.size() == 0) {
notifyAll();
}
this.queue.add(item);
}
public synchronized Object dequeue()
throws InterruptedException{
while(this.queue.size() == 0){
wait();
}
if(this.queue.size() == this.limit){
notifyAll();
}
return this.queue.remove(0);
}
}
推荐下面的方法
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by Lenovo on 2018/5/17.
*/
public class LinkedBlockingQueue<E> {
private final int capacity;
private Lock lock = new ReentrantLock();
private Condition unfull = lock.newCondition();
private Condition unEmpty = lock.newCondition();
private int count;
private LinkedList<E> queue;
public LinkedBlockingQueue() throws InterruptedException {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) throws InterruptedException {
this.capacity = capacity;
queue = new LinkedList<E>();
}
public void put(E e) throws InterruptedException {
lock.lock();
try {
while (count == capacity) {
unfull.await();//阻塞队列已满,等待
}
queue.add(e);
count++;
unEmpty.signal();
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {//队列为空,阻塞
unEmpty.await();
}
E e = queue.pop();
count--;
unfull.signal();
return e;
} finally {
lock.unlock();
}
}
}
使用阻塞队列
就是完成了生产者和消费者的模式
class myPAndC{
public static void main(String[] args) {
LinkedBlockingQueue<Integer> lq = new LinkedBlockingQueue<>(5);
new Thread(){
public void run(){
for(int i = 0; i < 10; i++){
lq.put(i);
}
}.start();
new Thread(){
public void run(){
for(int i = 0; i < 10; i++){
lq.take();
}
}.start();
}
}
使用读写锁实现一个缓存
public class Cache<String, V> {
private final Map<String, V> DATA_MAP = new HashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 读锁
*/
private final Lock readLock = readWriteLock.readLock();
/**
* 写锁
*/
private final Lock writeLock = readWriteLock.writeLock();
/**
* 读取缓存,若缓存不存在则查找数据库并放到缓存
* @param key
* @return
*/
public V get(String key) {
V value = null;
//读取缓存
readLock.lock(); // 1
try {
value = DATA_MAP.get(key); //2
} finally {
readLock.unlock(); // 3
}
// 缓存中存在,直接返回
if (Objects.nonNull(value)) { // 4
return value;
}
writeLock.lock(); //5
try {
// 再次验证是否为空,可能其他写线程已经查询过数据库写到缓存中了,后续再次获取到写锁的线程就不用再次查数据库
value = DATA_MAP.get(key); // 6
if (Objects.isNull(value)) { // 7
//模拟查询数据库
value = (V) "queryFromDB";
DATA_MAP.put(key, value);
}
} finally {
writeLock.unlock();
}
return value;
}
public V put(K key, V value) {
writeLock.lock();
try {
return DATA_MAP.put(key, value);
} finally {
writeLock.unlock();
}
}
}
更高效的方式:写锁读库后直接降级为读锁马上可以共享数据,一分钟都不独占。降级为读锁的意思就是写锁没释放就直接加读锁。
(读锁不能升级为写锁)
public V get(String key) {
readLock.lock();
System.out.println("开始读取缓存数据....." + Thread.currentThread().getName());
V value = DATA_MAP.get(key);
//以下一段是需要读库的逻辑。
if (Objects.isNull(value)) {
// 若 value 为空则先释放读锁,并且让该线程获取写锁,而其他线程只能等待该写锁释放
// 必须先释放读锁才能去获取写锁,读锁还没有释放,此时获取写锁,会导致写锁永久等待,最终导致相关线程都被阻塞,永远也没有机会被唤醒。锁的升级是不允许的,这个你一定要注意。
readLock.unlock();
writeLock.lock();
// 再次检查状态,因为其他线程可能已经获取到写锁且更新了状态(双重检查)
try {
if (Objects.isNull(value)) {
//模拟查询数据库
value = (V) "queryFromDB";
DATA_MAP.put(key, value);
}
// 通过在释放写锁之前获取读锁来实现锁降级,降级为读锁
readLock.lock();
} finally {
// 释放写锁,依然持有读锁
writeLock.unlock();
}
}
//当前还持有读锁,所以最后还要释放读锁
try {
System.out.println(Thread.currentThread().getName() + "-- 读 : {key:" + key + ",value: " + value + "}");
return value;
} finally {
System.out.println("结束读取缓存数据....." + Thread.currentThread().getName());
readLock.unlock();
}
}
}