【面试必备】手撕代码,你怕不怕? (3)

跟上面一种方式没有很大的差别,倒是代码更加简单通透,不过需要注意的是对阻塞队列添加失败的错误处理

消费者代码 public class Consumer implements Runnable { private BlockingQueue<Integer> queue; // 内存缓冲区 private static final int SLEEPTIME = 1000; public Consumer(BlockingQueue<Integer> queue) { this.queue = queue; } @Override public void run() { int data; Random r = new Random(); System.out.println("start consumer id = " + Thread.currentThread().getId()); try { while (true) { // 模拟延迟 Thread.sleep(r.nextInt(SLEEPTIME)); // 从阻塞队列中获取数据 if (!queue.isEmpty()) { data = queue.take(); System.out.println("consumer " + Thread.currentThread().getId() + " consume data:" + data + ", size:" + queue.size()); } else { System.out.println("Queue is empty, consumer " + Thread.currentThread().getId() + " is waiting, size:" + queue.size()); } } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } } } 主线程代码 public static void main(String args[]) throws InterruptedException { // 1.构建内存缓冲区 BlockingQueue<Integer> queue = new LinkedBlockingDeque<>(); // 2.建立线程池和线程 ExecutorService service = Executors.newCachedThreadPool(); Producer prodThread1 = new Producer(queue); Producer prodThread2 = new Producer(queue); Producer prodThread3 = new Producer(queue); Consumer consThread1 = new Consumer(queue); Consumer consThread2 = new Consumer(queue); Consumer consThread3 = new Consumer(queue); service.execute(prodThread1); service.execute(prodThread2); service.execute(prodThread3); service.execute(consThread1); service.execute(consThread2); service.execute(consThread3); // 3.睡一会儿然后尝试停止生产者 Thread.sleep(10 * 1000); prodThread1.stop(); prodThread2.stop(); prodThread3.stop(); // 4.再睡一会儿关闭线程池 Thread.sleep(3000); service.shutdown(); }

因为队列中添加和删除的操作比较频繁,所以这里使用LinkedBlockingQueue来作为阻塞队列,所以这里除了内存缓冲区有所不同以外,其他的都差不多...当然你也可以指定一个队列的大小;

总结以及改进

生产者-消费者模式很好地对生产者线程和消费者线程进行解耦,优化了系统整体的结构,同时由于缓冲区的作用,允许生产者线程和消费者线程存在执行上的性能差异,从一定程度上缓解了性能瓶颈对系统性能的影响;上面两种写法都是非常常规的写法,只能说能起码能在及格的基础上加个那么点儿分数,如果想要得高分可以去搜索搜搜 Disruptor 来实现一个无锁的生产者-消费者模型....这里就不提及了..

改进:上面的线程输出可能会有点儿不友好(不直观),因为我们这里是直接使用的线程的 ID 来作为输出,我们也可以给线程弄一个名字来作为输出,以上;

Part 2.排序算法

排序算法当然也算是重点考察的对象之一了,毕竟基础且偏算法,当然我们有必要去了解常见的排序算法以及它们采取了怎样的思想又是如何实现的还有复杂度的问题,但是这里的话,主要就提及两种考的比较常见的排序算法:冒泡快排,以及分别对它们进行的一些优化;

冒泡排序

冒泡应该是比较基础的一种算法,我们以从小到大排序为例,它的基础思想是:从第一个数开始直到数组倒数第二个数,每一轮都去比较数组中剩下的数,如果后面的数据更小则两数交换,这样一轮一轮的比较交换下来,最大的那个数也就“沉到”了数组的最后,最小的“冒”到了数组的最前面,这样就完成了排序工作;

基础算法代码(未优化)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdpfy.html