zoukankan      html  css  js  c++  java
  • JAVA面试题

    在这里我将收录我面试过程中遇到的一些好玩的面试题目

    第一个面试题:ABC问题,有三个线程,工作的内容分别是打印出“A”“B”“C”,需要做的就是让他们顺序的输出ABC  例如:ABCABCABCABC

    思路一:我觉得这个功能是需要封装的,而且能够做到,无论多少个线程都能够顺序打印出来,并且基本上不需要改任何代码。我的思路是首先封装一个工作的单元为Node,主要的工作就是打印和竞争锁并且记录加入工作的索引,然后还有一个ConcurrentHashMap储存工作状态。如果全部工作完了之后,由最后一个工作单元刷新状态。下面是实现的代码:

    package com.hotusm.concurrent.test;
    
    import java.util.Map;
    import java.util.TreeMap;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Created by luqibao on 2016/12/30.
     */
    public class ABCSample {
    
        private static ReentrantLock lock=new ReentrantLock();
    
        private static volatile TreeMap<Integer,Node> indexs=new TreeMap<>();
    
        public static void main(String[] args){
            ReentrantLock lock=new ReentrantLock();
            TreeMap<Integer,Node> indexs=new TreeMap<>();
            Node node1=new Node("A",lock,indexs,0);
            Node node2=new Node("B",lock,indexs,1);
            Node node3= new Node("C",lock,indexs,2);
            indexs.put(0,node1);
            indexs.put(1,node2);
            indexs.put(2,node3);
    
            node1.beforeWork();
            node2.beforeWork();
            node3.beforeWork();
    
    
            try{
                TimeUnit.SECONDS.sleep(3);
            }catch (Exception e){
    
                e.printStackTrace();
            }
    
            node1.start();
            node2.start();
            node3.start();
    
            synchronized (ABCSample.class){
    
                try{
                    ABCSample.class.wait();
                }catch (Exception e){
                    e.printStackTrace();
                }
    
            }
    
        }
    
        private static class WorkQueue{
    
            private Node tail;
            private Node head;
        }
    
        private static class Node extends Thread{
    
            private static volatile boolean isRefresh=false;
    
            private volatile Map<Integer,Node> maps;
            private volatile boolean isWorked=false;
            private final int index; //索引
    
            private String message;
            private volatile Lock readLock;
            private boolean isLast=false;
    
            public Node(String message,Lock lock,Map<Integer,Node> maps,int index){
                this.message=message;
                this.readLock=lock;
                this.maps=maps;
                this.index=index;
            }
    
            public int getIndex() {
                return index;
            }
    
            public void beforeWork(){
    
                readLock.lock();
                try{
                    if(index==maps.size()-1){
                        isLast=true;
                    }else{
                        isLast=false;
                    }
                }finally {
                    readLock.unlock();
                }
    
            }
            @Override
            public void run() {
                while (true){
                    readLock.lock();
                    if(isRefresh){continue;}
                    try{
                        if(this.index==0&&!this.isWorked){
                            System.out.print(this.message);
                            this.isWorked=true;
                        }else if(index!=0){
                            Node node= maps.get(index-1);
                            if(!node.isWorked){
                                System.out.print(node.message);
                                node.isWorked=true;
                            }else{
                                if(!this.isWorked){
                                    System.out.print(this.message);
                                    this.isWorked=true;
                                }else if(this.isWorked&&this.isLast){
                                    refresh();
                                }
                            }
                        }
                    }catch (Exception e){
                        Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }finally {
                        readLock.unlock();
                    }
    
                    try{
                        TimeUnit.SECONDS.sleep(1);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
    
                }
            }
    
            private void refresh(){
                isRefresh=true;
                for(Map.Entry<Integer,Node> map:maps.entrySet()){
                    map.getValue().isWorked=false;
                }
                isRefresh=false;
            }
        }
    }

    其实上面还有很多封装的地方,注册可以封装成方法,前置工作也是,可以用一个volatile修饰的boolean来标记,同理启动也是一样的,如果能够的话,最好能做到,使用的时候只需要调用一个register(msg) 和start()就可以了

    思路二:因为这是一个环形链接结构,所以我们可以通知将锁从head开始一直往后让其他获取一遍,下面代码:

    package com.hotusm.concurrent.test;
    
    import sun.misc.Unsafe;
    
    import java.lang.reflect.Field;
    import java.util.concurrent.CountDownLatch;
    
    /**
     * Created by luqibao on 2017/1/3.
     */
    public class ABCSample2 {
    
    
        public static void main(String[] args){
    
           WorkQueue queue= new WorkQueue();
           queue.addWork("A");
           queue.addWork("B");
           queue.addWork("C");
           queue.addWork("D");
           queue.addWork("E");
    
           queue.startWork();
    
    
            synchronized (ABCSample2.class){
    
                try{
                    ABCSample2.class.wait();
                }catch (Exception e){
                    e.printStackTrace();
                }
    
            }
        }
        private static class WorkQueue{
    
            private Node head;
            private Node tail;
            private int num=0;
    
            private static final Unsafe unsafe = getUnsafe();
            private static final long tailOffset;
            private static final long headOffset;
    
            static {
                try {
                    // 获取当前字段的内存位置  后来替换的地方需要使用
                    headOffset = unsafe.objectFieldOffset
                            (WorkQueue.class.getDeclaredField("head"));
                    tailOffset = unsafe.objectFieldOffset
                            (WorkQueue.class.getDeclaredField("tail"));
                } catch (Exception ex) { throw new Error(ex); }
            }
    
            private static Unsafe getUnsafe() {
                try {
    
                    Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
                    singleoneInstanceField.setAccessible(true);
                    return (Unsafe) singleoneInstanceField.get(null);
    
            } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
    
            protected void push(Node node){
    
                for (;;) {
                    Node t = tail;
                    if (t == null) {
                        if (compareAndSetHead(node))
                            num++;
                        tail = head;
                        break;
                    } else {
                        if (compareAndSetTail(t, node)) {
                            num++;
                            t.next = node;
                            break;
                        }
                    }
                }
            }
    
            /**
             *加入工作的顺序就是打印的顺序
             * @param message  打印的信息
             */
           public void addWork(String message){
                Node node=new Node(message);
                push(node);
            }
    
            /**
             * 开始进行工作
             */
            public void startWork(){
                Node.head=this.head;
                Node.tail=tail;
                Node.latch=new CountDownLatch(num);
                Node.currentWork=head;
    
                for (Node node=head;node!=null;node=node.next){
                    node.start();
                    Node.latch.countDown();
                }
            }
    
    
            public boolean compareAndSetTail(Node expect,Node newValue){
                return unsafe.compareAndSwapObject(this,tailOffset,expect,newValue);
            }
            public boolean compareAndSetHead(Node expect){
                return unsafe.compareAndSwapObject(this,headOffset,null,expect);
            }
    
        }
    
    
        private static class Node extends Thread{
    
            private static volatile Node currentWork;
    
            private static volatile Node head;
            private static volatile Node tail;
            private static volatile CountDownLatch latch;
    
            private String message;
            private Node next;
    
    
            public Node(String message) {
                this.message = message;
            }
    
            @Override
            public void run() {
    
                try {
                    latch.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                for(;;){
                    if(currentWork==this){
                        if(!"".equals(message)){
                            System.out.print(message);
                        }
                        if(this==tail){
                            currentWork=head;
                        }else{
                            currentWork=this.next;
                        }
                    }
                }
    
            }
        }
    }

    总的来说,我还是比较喜欢后面这种的,但是还是需要优化,比如在启动之后就不能够添加工作了,验证工作不能重复等等。

    第二个面试题:

    具体题目如下,
    
    -         Java开发
    
    -         功能要求:
    
    o   网络爬虫,可以使用少量的3方库,但是最好能够用自己写的代码
    
    o   加分点:使用多线程,注意同步和锁
    
    o   将豆瓣(book.douban.com)里的关于“互联网,编程,算法”方面的书籍数据抓下来,并且显示评分最高的前100本数据(要求评价人数超过2000,不足2000的就提取前100)
    
    o   代码和爬下的结果(excel文件)一并放在github里,链接发给你们,再转给我。

    这个面试题是一家500强的外企的题目,因为之前一直没有接触过爬虫方面,所以觉得比较有意思,代码在我们的github上面:https://github.com/Housum/crawl.git。

    因为当时时间比较紧(白天在项目),本来是想自己写网络和解析的,但是没时间,所以用的都是第三方的,反而觉得除了锁,其他的都是第三方框架的东西。面试官评价还行。

  • 相关阅读:
    《Typecript 入门教程》 3、接口
    《Typecript 入门教程》 2、访问控制符:public、private、protected、readonly
    《Typecript 入门教程》 1、类
    使用Visual Studio Code + Node.js搭建TypeScript开发环境
    call( )、apply( )
    Sublime Text Version 3.0,Build3143注册码
    angular指令笔记(一):ng-options
    java 约束配置文件和本地约束
    hadoop在zookeeper上的高可用HA
    Zookeeper简单初应用
  • 原文地址:https://www.cnblogs.com/zr520/p/6247909.html
Copyright © 2011-2022 走看看