面试手写非算法

设计模式

单例模式

  • 要使用静态方法和静态字段
  • 要写个私有的构造方法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();
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值