练手——蚂蚁二面笔试题

1. 起两个线程,交替打印奇数和偶数

一拿到这个题目有几个思路,1.共享变量,2.等待/通知机制,其实感觉考察的是进程通信的方式(6种)

共享变量实现如下:

public class oddAndEvenPrintThread {public static int cnt = 0;public static final int RANGE = 1000;public static List<Integer> ans = new ArrayList<>(RANGE);public static void main(String[] argus) {Thread oddPrinter = new Thread(() -> {while (cnt < RANGE) {if ((cnt & 1) == 1) {//System.out.println(cnt);ans.add(cnt);cnt++;}}}, "oddPrinter");Thread evenPrinter = new Thread(() -> {while (cnt < RANGE) {if ((cnt & 1) == 0) {//System.out.println(cnt);ans.add(cnt);cnt++;}}}, "evenPrinter");try {oddPrinter.start();evenPrinter.start();oddPrinter.join();evenPrinter.join();} catch (Exception e) {e.printStackTrace();}ans.forEach((a) -> System.out.print(a + " "));}
}

其实这里可以有几个优化的地方,1.用线程池代替单独的线程;2.当不满足cnt条件时可以加一个yield主动放弃cpu资源(这个见仁见智,我觉得没必要,多核cpu还会增加上下文切换的开销)

用线程池又写了一个版本:(此处如果面试还能再阐述下线程池的几个好处,绝对的加分项)

public class oddAndEvenPrintThread {public static int cnt = 0;public static final int RANGE = 1000;public static List<Integer> ans = new ArrayList<>(RANGE);public static final ExecutorService oddExecutorService = new ThreadPoolExecutor(1,1,60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>());public static final ExecutorService evenExecutorService = new ThreadPoolExecutor(1,1,60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>());public static void main(String[] argus) {Future oddTask = oddExecutorService.submit(() -> {while (cnt < RANGE) {if ((cnt & 1) == 1) {//System.out.println(cnt);ans.add(cnt);cnt++;}}//return “finish”; 通过future.get可以得到这边return的结果,因为默认的泛型是object,不限数据类型});Future evenTask = evenExecutorService.submit(() -> {while (cnt < RANGE) {if ((cnt & 1) == 0) {//System.out.println(cnt);ans.add(cnt);cnt++;}}});try {oddTask.get();evenTask.get();} catch (Exception e) {e.printStackTrace();}ans.forEach((a) -> System.out.print(a + " "));}
}

这里需要用submit代替execute,原因是submit的任务可以拿到Future的一个返回值,在主线程中我们可以通过futureTask.get操作获取到返回的结果值,若果任务未执行完则会阻塞


之后的第二个思路,用wait()和notify()实现,这个版本由于涉及到synchronize加锁效率较低,同时写的时候出现了下面的问题:

说明在自己线程里面,不能对自己去wait,而且此时去通知evenPrinter线程,会由于该线程定义的顺序关系无法运行,固需要换个思路在主线程里去做通知和等待,而这两个线程仅负责打印奇数和偶数,不做其他的判断(相当于把判断当前哪个线程打数字的工作交给了主线程),理清了思路不难发现写起来真的很麻烦,同时判断打奇数和偶数还要通过一个变量cnt去记录,那为什么不直接采用第一个版本呢?这个实现显然多此一举。

2.实现一个带超时时间的队列,队列中会自动剔除超时的数据

一个思路就是用一个linkedlist 作为队列的载体,再加上一个timer或者起一个线程去每隔几秒删除过期数据
另一个思路其实和redis的惰性删除相似,即在要使用的时候才检查是否过期,不过这个方法有一个弊端,当队列塞满的时候,需要去设置键的remove策略,因为有些键可能过期了,但没有被删除,此时还存放在队列里边。

此处直接用第一个思路实现,同时考虑到多线程环境(加分项),对相关方法做同步操作。

用timer或者thread去写大同小异:

public class QueueWithTimeOut{private LinkedList<Node> que;private Timer timer;private int size;private int curSize = 0;QueueWithTimeOut(int size, int delay, int interval) {this.size = size;this.que = new LinkedList();this.timer = new Timer();//使用Timer
//        timer.schedule(new TimerTask() {//            @Override
//            public void run() {//                scanAndDelete();
//            }
//        }, delay, interval);//使用threadThread thread = new Thread(() -> {try {Thread.sleep(delay);} catch (Exception e) {e.printStackTrace();}while (true) {try {scanAndDelete();Thread.sleep(interval);//注意此处要用类去调用静态方法,直接用实例thread调用会报其可能尚未实例化} catch (Exception e) {e.printStackTrace();}}}, "scanThread");thread.start();}public class Node {Object key;long timeout; //这里timeout存放的是currentTime + timeoutNode(Object key, long timeout) {this.key = key;this.timeout = timeout;}public long getTimeout() {return timeout;}}public synchronized void offer(Object key, long currentTime, long timeout) throws Exception{timeout += currentTime;if (curSize > size) throw new Exception("size 已满");Node node = new Node(key, timeout);que.offer(node);curSize++;}public synchronized Object poll() {if (!que.isEmpty()) return que.poll();elsereturn null;}private synchronized void scanAndDelete() {if (!que.isEmpty()) {int len = que.size();for (int i = 0;i<len;i++) {if (que.get(i).getTimeout() < getCurrentTime()) que.remove(i);}}elsereturn ;}private long getCurrentTime() {return System.currentTimeMillis();}
}

由于考虑到多线程,在方法里加了synchronized,性能并不高,更好的实现是用读写锁去优化,改写后的offer,poll,scanAndDelete方法如下:

private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();public  void offer(Object key, long currentTime, long timeout) throws Exception{timeout += currentTime;if (curSize > size) throw new Exception("size 已满");rwlock.writeLock().lock();Node node = new Node(key, timeout);que.offer(node);curSize++;rwlock.writeLock().unlock();
}public synchronized Object poll() {rwlock.readLock().lock();if (!que.isEmpty()) return que.poll();rwlock.readLock().unlock();return null;
}private void scanAndDelete() {rwlock.readLock().lock();if (!que.isEmpty()) {int len = que.size();for (int i = 0;i<len;i++) {if (que.get(i).getTimeout() < getCurrentTime()) {rwlock.writeLock().lock();que.remove(i);rwlock.writeLock().unlock();}}}rwlock.readLock().unlock();return ;
}

3.字符串匹配,KMP算法

之前一直反复忘KMP的写法,贴上自己整理的一篇理解:印象笔记——KMP

public class KMP {public static void main(String[] argus) {String  str = "abcabcdab";String  pattern = "abcdr";int i = 0, j = 0;int len_str = str.length(), len_pat = pattern.length();if (len_pat > len_str) return;int[] next = new int[len_pat];getNext(pattern, next);while (i < len_str && j < len_pat) {if (str.charAt(i) == pattern.charAt(j)) {i++;j++;} else {j = next[j];}}if (j == len_pat) System.out.println(i - j);elseSystem.out.println(-1);return;}public static void getNext(String pattern, int[] next){int k = -1, i = 0;next[0] = -1;while (i < pattern.length() - 1) {if (k == -1 || pattern.charAt(k) == pattern.charAt(i)) {k++;i++;next[i] = k;} else {k = next[k];}}}
}

小结

三道题共一小时,感觉在正常实现的基础上适当考虑多线程的情况,使用线程池代替线程会是个不错的加分项。接着还可以bb为什么要用线程池代替线程(1.便于对线程这类昂贵资源的管理;2.复用线程节省资源;3.提高响应速度,无需先创建个线程;4.线程挂了的话线程池会自动补充;5.单线程的话如果前面的任务执行时间过长会对后面任务产生影响,即timer的不足之一),后面4,5也是推荐用scheduledThreadPool代替timer的原因(因为timer底层是单线程的)

贴上用scheduledThreadPool实现timer的代码:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);QueueWithTimeOut(int size, int delay, int interval) {this.size = size;this.que = new LinkedList();this.timer = new Timer();
//        timer.schedule(new TimerTask() {//            @Override
//            public void run() {//                scanAndDelete();
//            }
//        }, delay, interval);scheduledThreadPool.schedule(()->{scanAndDelete();},interval, TimeUnit.MILLISECONDS);}

练手——蚂蚁二面笔试题相关推荐

  1. 分享一道蚂蚁金服笔试题

    之前有个朋友面试蚂蚁金服p7,他的笔试题我要了过来,我觉得这道题目还是比较经典的,有兴趣的朋友不妨先拿来热热身. 实现转账系统,给外部系统提供账户开户,充值,转账rpc服务,要求如下,- 账户类设计: ...

  2. TensorFlow练手项目二:基于循环神经网络(RNN)的古诗生成器

    基于循环神经网络(RNN)的古诗生成器 2019.01.02更新: 代码比较老了,当时的开发环境为Python 2.7 + TensorFlow 1.4,现在可能无法直接运行了.如果有兴趣,可以移步我 ...

  3. C学习杂记(二)笔试题:不使用任何中间变量如何将a、b的值进行交换

    常见的方法如下 void swap1(int *a, int *b) {int temp = *a;*a = *b;*b = temp; } 不使用中间变量的方法 void swap2(int *a, ...

  4. TensorFlow项目练手(二)——猫狗熊猫的分类任务

    项目介绍 通过猫狗熊猫图片来对图片进行识别,分类出猫狗熊猫的概率,文章会分成两部分,从基础网络模型->利用卷积网络经典模型Vgg. 基础网络模型 基础的网络模型主要是用全连接层来分类,比较经典的 ...

  5. Python——生成一个大文件ips.txt,要求1200行 ,每行随机为172.25.254.0/24段的ip(京东二面笔试题)

    代码块: from collections import Iterable f = open('/tmp/passwd') print(isinstance(f,Iterable)) for i,it ...

  6. MySQL笔试题练习及答案(一)

    第一题 题目描述 查找最晚入职员工的所有信息,为了减轻入门难度,目前所有的数据里员工入职的日期都不是同一天(sqlite里面的注释为–,mysql为comment) CREATE TABLE empl ...

  7. 记一次海康威视笔试题小练手

    今天老师在群里发了海康威视的校招信息,有点心动,上牛客找到面经看到有3道笔试题,就顺便试试,都是用js写. 第一题 <!DOCTYPE html> <html lang=" ...

  8. JAVA笔试题笔记(二)

    2016广联达笔试题 一.解释操作系统中heap和stack的区别 1.heap是堆,stack是栈. 2.stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的(Java中是由垃 ...

  9. 软件测试工程师笔试题带答案(二)

    如何对web系统进行全面测试? 一. 功能测试 1.链接测试 链接是Web应用系统的一个主要特征,它是在页面之间切换和指导用户去一些不知道地址的页面的主要手段.链接测试可分为三个方面.首先,测试所有链 ...

最新文章

  1. xxl-job Vs ElasticJob,谁牛?
  2. 解析Shell编程中的-----“去重“
  3. 网站推广——对于网站推广公司来说应如何帮助企业实现网站优化?
  4. 相角裕量的指令 matlab,[转载]什么是相角裕量、增益裕量?
  5. SpringBoot-@Conditional
  6. 算法--递归--走台阶问题(2种递归+递归改循环)
  7. 山东计算机专业的在职硕士,山东轻工业学院在职研究生计算机专业如何复习
  8. 世界名校、大厂人才汇聚,“马栏山杯”算法大赛打造 AI 视频竞技场
  9. css绘制向左三角形_CSS绘制三角形—border法
  10. JAVA中加密算法的简单使用入门
  11. 物联网是自动化还是计算机,物联网:自动化家里的一切
  12. emc整改措施及案例_EMC整改案例分享
  13. 360浏览器兼容性问题
  14. 【Matplotlib绘制图像目录】Python数据可视化之美
  15. Java常用设计模式-策略模式
  16. 将不规则时间段降雨量拆分合并到整点小时时间段
  17. 图形学(4)多边形的扫描转换(上)
  18. Idea创建SpringBoot搭建SSM框架项目,支持JSP页面
  19. python爬虫和定位_Python网络爬虫实战(三)照片定位与B站弹幕
  20. 英国HAWKER叉车蓄电池48V360AH/3PzS360 林德叉车R14电池霍克电瓶

热门文章

  1. 2022年来了,从Python定制一份日历开始吧!
  2. 基于单片机的秒表计时器系统设计(#0400)
  3. 基于freeradius的无线认证
  4. java-利用反射做一个将javabean对象转为json的小工具(v1)
  5. Coffee Chicken
  6. R语言使用逻辑回归分类算法
  7. Element el-icon 图标组件详解
  8. 修改功能测试的测试点
  9. JAVA中传值与“传引用“辨析
  10. mysql重新设置主键生成策略为auto_increment时报错:resulting in dup