logo资料库

大数据面试宝典-精简版.pdf

第1页 / 共115页
第2页 / 共115页
第3页 / 共115页
第4页 / 共115页
第5页 / 共115页
第6页 / 共115页
第7页 / 共115页
第8页 / 共115页
资料共115页,剩余部分请下载后查看
java基础篇
问题1
问题2
问题3
问题4
JVM篇
问题1
问题2
数据库篇
问题1
问题2
问题3
--问题4
--问题5
--问题6
--问题7
hadoop篇
问题1
问题2
--问题3
问题4
问题5
问题6
问题7
问题8
问题9
问题10
问题11
问题12
问题13
问题14
问题15
问题16
问题17
问题18
问题19
hive篇
问题1
问题2
问题3
问题4
问题5
问题6
问题7
问题8
问题9
问题10
hbase篇
问题1
问题2
问题3
问题4
问题5
flume篇
问题1
zookeeper篇
问题1
问题2
问题3
问题4
问题5
问题6
kafka篇
问题1
问题2
问题3
问题4
问题5
问题6
问题7
scala篇
问题1
spark篇
问题1
--问题2
问题3
问题4
问题5
问题6
问题7
问题8
问题9
问题10
问题11
问题12
问题13
问题14
问题15
问题16
redis篇
问题1
问题2
机器学习篇
问题1
--问题2
--问题3
--问题4
--问题5
--问题6
业务相关
--问题1
问题2
--问题3
问题4
问题5
问题6
问题7
问题8
问题9
问题10
问题11
java基础篇 问题1 列举Java中的基本数据类型 1)四种整数类型(byte、short、int、long): byte:8 位,用于表示最小数据单位,如文件中数 据,-128~127 short:16 位,很少用,-32768 ~ 32767 int:32 位、最常用,-2^31-1~2^31 (21 亿) long:64 位、次常用 注意事项: int i=5; // 5 叫直接量(或字面量),即 直接写出的常数。 整数字面量默 认都为 int 类型,所以在定义的 long 型数据后面加 L或 l。 小于 32 位数的变量,都按 int 结果计算。 强转符 比数学运算符优先级高。见常量与变量中的例子。 2)两种浮点数类型(float、double): float:32 位,后缀 F 或 f,1 位符号位,8 位指数,23 位有效尾数。 double:64 位,最常用,后缀 D 或 d,1 位符号位,11 位指数,52 位有效尾 注意事项: 二 进 制 浮 点 数 : 1010100010=101010001.02=10101000.102^10(2次方)=1010100.0102^11(3 10101000102^1010(10次方) 尾数: . 1010100010 指数:1010 基数:2 浮点数字面量默认都为 double 类 型,所以在定义的 float 型数据后面加F 或 f;double 类型可不写后缀,但在小数计算中一定要写 D 或 X.X float 的精度没有 long 高,有效位数(尾数)短。 float 的范围大于 long 指数可以很大。 浮点数是不精确 的,不能对浮点数进行精确比较。 )= . 3)一种字符类型(char): char:16 位,是整数类型,用单引号括起来的 1 个字符(可以是一个中文字 符),使用 Unicode 码代表字符,0~2^16-1(65535) 。 注意事项: 不能为 0个字符。 转义字符:\n 换行 \r 回车 \t Tab 字符 " 双引号 \ 表示一个\ 两字符 char 中间用“+”连接,内部先把字符转成 int 类型,再进行加 法运算,char 本质就是个数!二进制的,显示的时候,经过“处理”显示为字符。 4)一种布尔类型(boolean):true 真 和 false 假。 5)类型转换: char--> 自动转换:byte-->short-->int-->long-->float-->double 强制转换:①会损失精度, 产生误差,小数点以后的数字全部舍弃。②容易超过取值范围。 6)记忆:8位:Byte(字节型) 16位:short(短整型)、char(字符型) 32位:int(整型)、float(单 精度型/浮点型) 64位:long(长整型)、double(双精度型) 最后一个:boolean(布尔类型 问题2 sdfas ArrayBlockingQueue的原理和底层实现的数据结构 : ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列,可以按照 FIFO(先进先出)原则对元 素进行排序。 线程安全是指,ArrayBlockingQueue内部通过“互斥锁”保护竞争资源,实现了多线程对竞争资源的互斥 访问。而有界,则是指ArrayBlockingQueue对应的数组是有界限的。 阻塞队列,是指多线程访问竞争 资源时,当竞争资源已被某线程获取时,其它要获取该资源的线程需要阻塞等待;所谓公平的访问队列 是指阻塞的线程,可以按照阻塞的先后顺序访问队列,先阻塞的线程先访问ArrayBlockingQueue队 列。非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格, 有可能先阻塞的线程最后才能够访问队列。然而为了保证公平性,通常会降低吞吐量。 次 方
1. ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须 制定容量大小。 并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最 长的队列最优先能够访问队列。 2. ArrayBlockingQueue内部通过Object[]数组保存数据的,也就是说ArrayBlockingQueue本质上是 通过数组实现的。ArrayBlockingQueue的大小,即数组的容量是在创建创建 ArrayBlockingQueue时候指定的。 3. 如下图所示,ArrayBlockingQueue和ReentrantLock是组合关系,ArrayBlockingQueue中包含一 个ReentrantLock对象。ReentrantLock是可重入的互斥锁。ArrayBlockingQueue就是根据 ReentrantLock互斥锁实现"多线程对共享资源的访问"。ReentrantLock分为公平锁和非公平锁, 关于具体使用公平锁还是非公平锁,在创建ArrayBlockingQueue时可以指定;而且, ArrayBlockingQueue默认会使用非公平锁。 4. ArrayBlockingQueue和Condition是组合关系,ArrayBlockingQueue中包含两个Condition对象 (notEmpty和notFull)。使用通知模式实现:所谓通知模式,当生产者往满的队列里面添加元素的时 候,会阻塞生产者(调用Condition notFull.await()进行等待);当消费者消费了一个队列中的元 素后,会通知(调用Condition notFull.signal()唤醒生产者)生产者当前队列可用。反之,当消费者 消费的时候,发现队列是空的,则消费者会被阻塞(通过Condition的 notEmpty.await()进行等 待),当生产者插入了队列中的一个元素后,则会调用notEmpty.signal()唤醒消费者继续消费。 ArrayBlockingQueue的数据结构如下: ArrayBlockingQueue方法列表:  
// 创建一个带有给定的(固定)容量和默认访问策略的 ArrayBlockingQueue。 ArrayBlockingQueue(int capacity) // 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue。 ArrayBlockingQueue(int capacity, boolean fair) // 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue,它最初包含给定 collection 的元素,并以 collection 迭代器的遍历顺序添加元素。 ArrayBlockingQueue(int capacity, boolean fair, Collection c) // 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true, 如果此队列已满,则抛出 IllegalStateException。 boolean add(E e) // 自动移除此队列中的所有元素。 void clear() // 如果此队列包含指定的元素,则返回 true。 boolean contains(Object o) // 移除此队列中所有可用的元素,并将它们添加到给定 collection 中。 int drainTo(Collection c) // 最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中。 int drainTo(Collection c, int maxElements) // 返回在此队列中的元素上按适当顺序进行迭代的迭代器。 Iterator iterator() // 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true, 如果此队列已满,则返回 false。 boolean offer(E e) // 将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间。 boolean offer(E e, long timeout, TimeUnit unit) // 获取但不移除此队列的头;如果此队列为空,则返回 null。 E peek() // 获取并移除此队列的头,如果此队列为空,则返回 null。 E poll() // 获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。 E poll(long timeout, TimeUnit unit) // 将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。 void put(E e) // 返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的其他元素数量。 int remainingCapacity() // 从此队列中移除指定元素的单个实例(如果存在)。 boolean remove(Object o) // 返回此队列中元素的数量。 int size() // 获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。 E take() // 返回一个按适当顺序包含此队列中所有元素的数组。 Object[] toArray() // 返回一个按适当顺序包含此队列中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 T[] toArray(T[] a) // 返回此 collection 的字符串表示形式。 String toString()   总结下上面的方法:  
*非阻塞队列中的方法: * *  抛出异常的方法 Exception in thread "main" java.lang.IllegalStateException: Queue full *1. add(e) throw exception,将元素e插入到队列的末尾,如果插入成功,则返回true,如果插入 失败 (队列已经满) 抛出异常 *2. remove(e) throw exception,移除队首元素,若移除成功,则返回true;若移除失败(队列 为空)则抛出异常 *3. element() throw exception 获取队列首元素,若获取成功,则返回首元素,否则抛出异常 java.util.NoSuchElementException * *  返回特定值的方法 * * 1.offer(E e),将元素e插入到队列末尾,如果插入成功,则返回true,如果插入失败(队列已 满),返回false * 2.poll(E e),移除并获取队首元素,若成功,则返回队首元素,否则返回null * 3.peek(E e),获取队首元素,若成功,则返回队首元素,否则则返回null *  可以指定TimeOut: * * 3.offer(E e,long timeout, TimeUnit unit):向队列尾部存入元素e,如果队列满,则等待 一定的时间,当达到timeout时候,则返回false,否则返回true 4.poll(long timeout, TimeUnit unit):从队首获取元素,如果队列为空,则等待一定时间, 当达到timeout时,如果没有取到,则返回null,如果取到则返回取到的元素 阻塞队列中的几个重要方法: * 1.put(E e) :用于队列尾部存入元素e,如果对满,则等待。 * 2.take():用于从对列首取出元素e,如果队列为空,则等待 注意:阻塞队列包括了非阻塞队列中的大部分方法,上面列举的5个方法在阻塞队列中都存在,但是要注 意这5个方法在阻塞队列中都进行了同步措施,都是线程安全的。   ArrayBlockingQueue源码分析(JDK1.7) 下面从ArrayBlockingQueue的创建,添加,取出,遍历这几个方面对ArrayBlockingQueue进行分析。 1.创建:   /**     * Creates an {@code ArrayBlockingQueue} with the given (fixed)     * capacity and default access policy.     *     * @param capacity the capacity of this queue     * @throws IllegalArgumentException if {@code capacity < 1}       只是指定ArrayBlockingQueue的容量,默认采用非公平互斥锁 */ public ArrayBlockingQueue(int capacity) { this(capacity, false); }  /**     * Creates an {@code ArrayBlockingQueue} with the given (fixed)
    * capacity and the specified access policy.     *     * @param capacity the capacity of this queue     * @param fair if {@code true} then queue accesses for threads blocked     *       on insertion or removal, are processed in FIFO order;     *       if {@code false} the access order is unspecified.     * @throws IllegalArgumentException if {@code capacity < 1}       指定容量和ReetrantLock的类型是否为公平锁创建阻塞队列     */    public ArrayBlockingQueue(int capacity, boolean fair) {        if (capacity <= 0)            throw new IllegalArgumentException();        this.items = new Object[capacity];        lock = new ReentrantLock(fair);        notEmpty = lock.newCondition();        notFull =  lock.newCondition();   } 上面源码进行说明: items是保存“阻塞队列”数据的数组。它的定义如下: /** The queued items */   final Object[] items; fair是“可重入的独占锁(ReentrantLock)”的类型。fair为true,表示是公平锁;fair为false,表示是非公 平锁。notEmpty和notFull是锁的两个Condition条件。它们的定义如下:    /** Main lock guarding all access */    final ReentrantLock lock;    /** Condition for waiting takes */    private final Condition notEmpty;    /** Condition for waiting puts */    private final Condition notFull; 说明:Lock的作用是提供独占锁机制,来保护竞争的资源;而Condition是为了更精细的对锁进行控 制,但是依赖于lock,通过某个条件对多线程进行控制。 notEmpty表示"锁的非空条件"。当某线程想从队列中获取数据的时候,而此时队列中的数据为空,则该 线程通过notEmpty.await()方法进行等待; 当其他线程向队列中插入元素之后,就调用notEmpty.signal()方法进行唤醒之前等待的线程。同理, notFull表示“锁满的条件“。当某个线程向队列中插入元素 ,而此时队列已满时,该线程等待,即阻塞通过notFull.wait()方法;其他线程从队列中取出元素之后, 就唤醒该等待的线程,这个线程调用notFull.signal()方法。 2.添加:    /**     * Inserts the specified element at the tail of this queue, waiting     * for space to become available if the queue is full.     *     * @throws InterruptedException {@inheritDoc}
    * @throws NullPointerException {@inheritDoc}       添加元素,当队列满的时候,该线程等待,即阻塞。     */    public void put(E e) throws InterruptedException {        //校验插入的元素不能为null        checkNotNull(e);        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            //队列满的时候            while (count == items.length)                //线程调用await方法阻塞                notFull.await();            insert(e);       } finally {            lock.unlock();       }   }  /** items index for next take, poll, peek or remove    下一个被取出元素的索引 */    int takeIndex;    /** items index for next put, offer, or add       下一个被添加元素的索引    */    int putIndex;    /** Number of elements in the queue       队列中的元素的个数     */    int count;  /**     * Inserts element at current put position, advances, and signals.     * Call only when holding lock.     */    private void insert(E x) {        items[putIndex] = x;        putIndex = inc(putIndex);        //队列中的元素个数        ++count;        //唤醒notEmpty Condition锁上面等待的线程,告诉该线程队列不为空了,可以消费了        notEmpty.signal();   }
/**     * Circularly increment i.       队列中的元素个数==队列的长度的时候,队列满,则设置下一个被添加元素的索引为0     */    final int inc(int i) {        return (++i == items.length) ? 0 : i;   } 2.取出: public E take() throws InterruptedException {        final ReentrantLock lock = this.lock;        //获取锁,如果当前线程是中断状态,则抛出interruptedException异常        lock.lockInterruptibly();        try {            //队列为空的时候,则线程一直阻塞等待            while (count == 0)                notEmpty.await();            //取元素            return extract();       } finally {            lock.unlock();       }   } /**     * Extracts element at current take position, advances, and signals.     * Call only when holding lock.     */    private E extract() {        final Object[] items = this.items;        //强制将元素转化为"泛型E"        E x = this.cast(items[takeIndex]);        items[takeIndex] = null;        //设置下一个被取出元素的索引        takeIndex = inc(takeIndex);        //将队列中的元素-1        --count;        //唤醒notFull条件上面等待的线程,告诉该线程队列不是满的了,可以添加元素了        notFull.signal();        return x;   } 问题3 了解fail-fast机制么 在JDK的Collection中我们时常会看到类似于这样的话: 例如,ArrayList:
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做 出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为 提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应 该仅用于检测 bug。 HashMap中: 注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任 何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依 赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错 误。 在这两段话中反复地提到”快速失败”。那么何为”快速失败”机制呢? “快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改 变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。例如:假设存在两个线程 (线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结 构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。 fail-fast产生原因 fail-fast产生的原因就在于程序在对 collection 进行迭代时,某个线程对该 collection 在结构上对其 做了修改,这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。 要了解fail-fast机制,我们首先要对ConcurrentModificationException 异常有所了解。当方法检测 到对象的并发修改,但不允许这种修改时就抛出该异常。同时需要注意的是,该异常不会始终指出 对象已经由不同线程并发修改,如果单线程违反了规则,同样也有可能会抛出改异常。 诚然,迭代器的快速失败行为无法得到保证,它不能保证一定会出现该错误,但是快速失败操作会 尽最大努力抛出ConcurrentModificationException异常,所以因此,为提高此类操作的正确性而编 写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅 用于检测 bug。 fail-fast解决办法 这里有两种解决方案: 方案一:在遍历过程中所有涉及到改变modCount值得地方全部加上synchronized或者直接使用 Collections.synchronizedList,这样就可以解决。但是不推荐,因为增删造成的同步锁可能会阻塞 遍历操作。 方案二:使用CopyOnWriteArrayList来替换ArrayList。推荐使用该方案。 CopyOnWriteArrayList为何物?ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。 该类产生的开销比较大,但是在两种情况 下,它非常适合使用。1:在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时。2:当 遍历操作的数量大大超过可变操作的数量时。遇到这两种情况使用CopyOnWriteArrayList来替代 ArrayList再适合不过了。那么为什么CopyOnWriterArrayList可以替代ArrayList呢? 第一、CopyOnWriterArrayList的无论是从数据结构、定义都和ArrayList一样。它和ArrayList一 样,同样是实现List接口,底层使用数组实现。在方法上也包含add、remove、clear、iterator等方 法。 第二、CopyOnWriterArrayList根本就不会产生ConcurrentModificationException异常,也就是它 使用迭代器完全不会产生fail-fast机制。
分享到:
收藏