美文网首页
java基础面试题总结(2)—— JDK基础

java基础面试题总结(2)—— JDK基础

作者: 天渊hyominnLover | 来源:发表于2018-08-15 10:55 被阅读5次

    JAVA SE基础

    1.ArrayList、LinkedList、Vector的特性及存储性能(持续更新)

    • ArrayList和Vector都继承了AbstractList,前者线程不安全,后者线程安全
    • ArrayList基于数组结构实现,查找速度快,插入和删除速度慢,LinkedList则相反,因为其是基于双向链表实现
    • ArrayList通过实现AbstractList,能够随机访问,而LinkedList实现的是AbstractSequentialList,仅能顺次访问

    2.HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap(持续更新)

    • HashMap线程不安全,Hashtable对每个操作都进行同步,线程安全;前者可以存在一个null的key,value可以为null,后者不能为null

    3.简述JVM内存结构中的heap和stack(持续更新)

    • heap即堆内存,存放对象数据,自动增加容量,存取较慢,线程共享
    • stack即栈内存,存放方法的局部变量、基本数据类型、对象引用、方法调用登,线程独占

    4.GC是什么? 为什么要有GC?

    • GC是Garbage Collection的缩写,即垃圾回收
    • 这里所谓的垃圾,指的是那些不再被使用的对象,JVM的垃圾回收机制使得开发人员从无聊、容易犯错的手动释放内存资源的过程中解放出来。
    • 开发人员可以更加专注的进行业务功能的开发,而资源回收的工作交由更加专业的垃圾回收机制自动完成

    5.关于String的知识点

    1. String s = new String("xyz"); 创建了几个String Object? :
      创建了2个String对象,首先传入的"xyz"是一个String对象,然后new的过程又创建了一个String对象

    6.abstract的方法是否可以是static的?是否可以是synchronized的?

    • 不能是static的:抽象方法是不能分配内存的,而static的方法在类加载时就已经分配内存,所以不能共存
    • 不能是synchronized的:方法同步的时候需要获取该方法所属实例对象的同步锁,但是abstract的方法是没有所属实例对象的,所以无法synchronized

    7.lock与Synchronize的区别?lock的基本使用方法及特性(持续更新)

    • Lock是一个接口,需要进行同步操作时,可实例化一个Lock(ReentrantLock),当前线程即占有这个Lock对象锁,而不像Synchronized那样需要制定持有某个特定的对象锁,这样能够避免Synchronized容易出现的死锁情况
    • 线程无法自动释放Lock,需要手动unlock(通常在finally)中,因此如果不手动释放的话也会造成死锁
    • Lock对象通过.lock()方法让线程占有锁,也可以通过lock.tryLock(long timeout, TimeUnit unit)方法来指定获取锁的时间,如果超时则返回false,放弃获取锁(也就放弃执行某同步方法)
    • Lock实现同步的方式也是CAS机制,ReentrantLock中执行lock()操作的是内部抽象类Sync的实现,ReentrantLock实例化时默认是采用非公平锁(NonfairSync)这个实现:
            final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
    

    可以看出,首先采用CAS机制来修改对象头中的锁标志位,CAS机制有三个重要参数:内存位置(V)、预期原值(A)和新值(B), 其中0就是期望的标志位原值A,1就是期望的标志位新值B;最终会调用一个native方法compareAndSwapInt()来使用CPU的CAS机制进行CAS操作,来不断尝试获取锁

    • Lock的CAS机制是一种典型的乐观锁机制,可以避免线程进入阻塞状态造成线程上下文切换降低性能;但是锁竞争太激烈的话,反而会造成性能低下;
    • Lock的Condition对象用法:使用Condition对象可以实现传统的wait()和notify()的目的,用Condition实现生产者-消费者模式:
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 阻塞栈,用于存放Mantou对象
     */
    public class SyncStack {
        volatile int index = 0;//栈内当前Mantou数量,需保证线程可见
        Lock lock = new ReentrantLock();//初始化锁
        Condition condition = lock.newCondition();//初始化Condition对象用于线程间通信
        Mantou[] mantous;
    
        public SyncStack(int size) {
            //初始化栈内容器的大小
            mantous = new Mantou[size];
        }
    
        /**
         * 将Mantou放入栈中,每放一个,index就增加一个,然后将等待池中的线程唤醒
         * 执行该方法前若检测若index已满则执行await,执行push()的线程进入Lock的等待池
         * @param mantou
         */
        public void push(Mantou mantou){
            //获取同步锁
            lock.lock();
            try{
                while(index == this.mantous.length){
                    condition.await();
                }
                this.mantous[index] = mantou;
                index++;
                condition.signalAll();
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    
        /**
         * 将栈顶的Mantou拿出来,每拿一个,index就减少一个,然后将等待池中的线程唤醒
         * 执行该方法前若检测index为空则执行await,执行get()的线程进入Lock等待池
         * @return
         */
        public Mantou get(){
            lock.lock();
            try{
                while(index == 0){
                    //设置等待时间,一旦超时就返回false,退出该方法
                    if(!condition.await(30, TimeUnit.SECONDS)){
                        return null;
                    }
                }
                index--;
                condition.signalAll();
                return this.mantous[index];
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            return null;
        }
    }
    
    public class Mantou {
        int id;
        String owner;
        public Mantou(int id, String owner){
            this.id = id;
            this.owner = owner;
        }
    
        @Override
        public String toString() {
            return owner + "的" +String.valueOf(id);
        }
    }
    
    /**
     * 生产者线程,持续调用push方法向阻塞栈放入Mantou对象
     */
    public class Producer implements Runnable{
        SyncStack syncStack;
        public Producer(SyncStack syncStack) {
            this.syncStack = syncStack;
        }
        @Override
        public void run() {
            //持续放入8个Mantou
            for(int i = 0; i<8; i++){
                Mantou mantou = new Mantou(i, Thread.currentThread().getName());
                syncStack.push(mantou);
                System.out.println(Thread.currentThread().getName()+":生产了" + mantou + "号馒头!!!");
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    /**
     * 消费者线程,持续调用get方法从阻塞栈取出Mantou对象
     */
    public class Consumer implements Runnable{
        SyncStack syncStack;
        public Consumer(SyncStack syncStack) {
            this.syncStack = syncStack;
        }
        @Override
        public void run() {
            Mantou mantou;
            //一旦获取Mantou超时,则退出
            while((mantou = syncStack.get())!=null) {
                System.out.println(Thread.currentThread().getName() + ":拿到了" + mantou + "号馒头!!!");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ":靠!居然没有了!艹!走了!");
        }
    }
    

    测试:

        public static void main(String[] args){
            SyncStack syncStack = new SyncStack(10);
            Consumer consumer = new Consumer(syncStack);
            Producer producer = new Producer(syncStack);
            Thread consumerThread1 = new Thread(consumer);
            Thread consumerThread2 = new Thread(consumer);
            Thread consumerThread3 = new Thread(consumer);
            Thread producerThread1 = new Thread(producer);
            Thread producerThread2 = new Thread(producer);
            consumerThread1.setName("消费者1");
            consumerThread2.setName("消费者2");
            consumerThread3.setName("消费者3");
            producerThread1.setName("生产者甲");
            producerThread2.setName("生产者已");
            producerThread1.start();
            producerThread2.start();
            consumerThread1.start();
            consumerThread2.start();
            consumerThread3.start();
    

    跟传统的wait和notifyAll方式大同小异

    8. 反射中Class.forName()和ClassLoader.loadClass()的区别

    反射中,Class.forName()和ClassLoader的方法loadClass()都可以通过类名加载类并返回类的Class对象,但是两者有一些区别:

    • Class.forName()实际调用的是forName0(className, true, ClassLoader.getClassLoader(caller), caller),这个过程默认将进行类加载->链接->初始化这一系列过程
    • ClassLoader的方法loadClass()实际调用的是ClassLoader.loadClass(className,false)这个方法,第二个参数表示不进行链接及初始化,仅仅进行类加载
    • 所有如果加载某个类需要进行链接及初始化操作,就需要调用Class.forName()这个方法来获取Class对象

    相关文章

      网友评论

          本文标题:java基础面试题总结(2)—— JDK基础

          本文链接:https://www.haomeiwen.com/subject/udvibftx.html