美文网首页
2021-08-20

2021-08-20

作者: KD小帅 | 来源:发表于2021-08-17 14:59 被阅读0次

    用什么Map可以保证线程安全,为什么?ConcurrentHashMap为什么能保证线程安全?1.7和1.8原理有什么差异。

    JDK用HashTable来保证线程安全,缺点:将整个Hash表锁住,性能很低。

    ConcurrentHashMap可以保证线程安全,jdk1.7中是采用Segment + HashEntry + ReentrantLock的方式进行实现的,而1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现。


    有多少种单例模式,枚举算不算单例,单例模式中不用volatile会导致什么问题?volatile特性是什么?为什么android中不推荐使用枚举。

    单例顾名思义就是程序运行中,最多只能有一个实例化的对象

    懒汉式、饿汉式的区别:区别实际上体现在加载时间上,饿汉式是在类加载的时候就加载;懒汉式是需要用到该实例时才加载。使用饿汉式的话,不管你用不用得到这个实例,它都会在类加载的时候加载;假如我一直没使用到这个实例,是不是就浪费资源了? 使用懒汉式的话,用到时候才会去加载,资源的利用上更为合理。

    总结:如果确定某个单例一定会用上,饿汉式是一种很合适方法;如果不一定会用的某个单例,懒汉式是比较合适的方法。

    饿汉式 方法1:静态变量方式(线程安全)

    /*1.将构造方法私有化,外部无法使用new构造方法创建实例*/

    private Singleton1() {}

    /*2.内部创建对象*/

    private static Singleton1 instence = new Singleton1();

    /*3.对外获取实例的方法*/

    public static Singleton1 getInstence() { return instence; } 

    方法2:静态代码块方式(线程安全)

    /*1.将构造方法私有化,外部无法使用new*/

    private Singleton2() {}

    /*2.内部静态代码块中创建对象*/

    private static Singleton2 instence;

    static { instence = new Singleton2(); }

    /*3.对外获取实例的方法*/

    public static Singleton2 getInstence() { return instence; }

    懒汉式

    方法1:双重检查(线程安全)这个方法比较常用

    /*1.将构造方法私有化,外部无法使用*/

    new private Singleton6() {}

    /*2.创建类成员变量instence*/

    private static volatile Singleton6 instence;

    /*3.对外获取实例的方法,双重判断*/

    public static Singleton6 getInstence() {

        if(instence == null) {

            synchronized (Singleton6.class){

                if (instence == null){

                    instence = new Singleton6();

                }

            }

        }

    return instence;

    方法2:静态内部类(线程安全)这个方法比较常用

      /*1.将构造方法私有化,外部无法使用*/

    new private Singleton7() {}

    /*2.编写一个静态内部类*/

    private static class SingletonInstence {

        private static final Singleton7 INSTENCE = new Singleton7();

    }

    /*3.提供一个静态公有方法,直接返回SingletonInstence*/

    public static Singleton7 getInstence() { return SingletonInstence.INSTENCE; } 

    方法3:枚举(线程安全,还可以防止反序列化重新生成的对象,推荐使用)这个方法被大佬推荐使用,至于为啥博主暂时未去了解

    public class Singleton8{

    }

    enum Sing{

    insance;

    public void method(){}

    }

    Volatile是Java虚拟机提供的轻量级的同步机制(三大特性)1.保证可见性2.不保证原子性3.有序性(禁止指令重排)

    枚举会牺牲执行的速度和并大幅增加文件体积。这也是性能优化中减少OOM的一个方面。

    implementation 和 api的区别是什么?

    implementation可以让module在编译时隐藏自己使用的依赖,减少build时间,但是在运行时这个依赖对所有模块

    是可见的。而api与compile一样,无法隐藏自己使用的依赖。

    Glide的缓存是怎么设计的?为什么要用弱引用

    Glide 的缓存可以分为两种,一种内存缓存,一种是硬盘缓存;其中内存缓存又包含 弱引用 和 LruCache ;而硬盘缓存就是 DiskLruCache

    为啥要用弱引用

    我们知道,glide 是用弱引用缓存当前的活跃资源的;为啥不直接从 LruCache 取呢?原因猜测如下:

    这样可以保护当前使用的资源不会被 LruCache 算法回收

    使用弱引用,即可以缓存正在使用的强引用资源,又不阻碍系统需要回收的无引用资源。

    A 跳转到 B页面,两个页面的生命周期怎么走?什么情况下A的stop()不会执行。

    当A跳转到B的时候,A先执行onCreate -> onStart -> onResume -> onPause,然后居然是B再执行onCreate -> onStart -> onResume,最后才执行A的onStop!!!

    当B按下返回键,B先执行onPause,然后居然是A再执行onRestart -> onStart -> onResume,最后才是B执行onStop  -> onDestroy!!!

    原理:A跳转B当调用startActivity时,向当前栈中添加activityB, activityA 会执行 onPause,此时 activityA 处于失去焦点但可见的状态,因为 activityB 还没开始绘制,所以activityA不会回调 onStop方法,接着 activityB 会开始执行 onCreate -> onStart -> onResume ,我们都知道Activity 是在 onResume 中开始绘制界面,当 activityB执行完 onResume后,此时 activityA 处于不可见的状态,即按图上所示,activityA 会回调 onStop 方法。这也就是在activityA 跳转 activityB 时,A的onStop 最后执行。B 返回 A同理,在activityB中按下返回键,activityB会先执行 onPause ,此时B还处于可见状态,A 会执行 onRestart -> onStart -> onResume ->,此时A 已经处于可见状态了,B 不可见,开始执行 onStop -> onDestroy 方法。这样解释就正确了! 

    ActivityB 为 dialog 式或者 半透明背景 ,当A跳转到B的时候,A先执行onPause,然后居然是B再执行onCreate -> onStart -> onResume。(注意:A的 onStop 不会执行)

    当B按下返回键,B先执行onPause,然后是A只会执行 onResume,最后 B 执行onStop  -> onDestroy

    Activity 的4中启动模式分别是什么,有什么不同。

    standard:在Activity的栈中无论该活动有没有加入栈,活动就会被创建。

    singleTop:只要被创建的活动不位于栈的顶部,该活动就会被创建入栈。如果将要被创建的活动位于栈的顶部,该活动的实例就不会被创建。测试方法,把上面的模式直接改成singleTop模式,MainActivty往自己身上跳转就不会从新创建一个新的实例,会重用之前在栈顶中的实例。如果是MainActivty跳转到SecondActivty, 然后SecondActivity再次跳转到MainActivty, 那么此时的MainActivity将会被创建,因为栈顶是SecondActivity。

    singleTask:单任务模式,这个也不难理解,如果从MainActivty跳转到SecondActivity, 如果再从SecondActivty跳转到MainActivity, 在单任务模式下MainActivity已经在栈中,就会把它之前的Activity出栈,使其处于在栈顶活跃的位置。

    singleInstance:,这个比较特殊,被设置成singleInstance的Activity将会放入另一个栈中,因为这样为了便于共用。上面3中模式位于同一个栈中。下方ThirdActivity跳转到一个加载模式为singleInstance的Activity中。

    GC原理,有哪几种GC方式

    GC垃圾收集,Java提供的GC可以自动监测对象是否超过作用域从而达到自动回收内存的目的。

    (1)强引用(Strong Reference):如“Object obj = new Object()”,这类引用是Java程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。

      (2)软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2之后提供了SoftReference类来实现软引用。

      (3)弱引用(Weak Reference):它也是用来描述非须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。

      (4)虚引用(Phantom Reference):最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2之后提供了PhantomReference类来实现虚引用。

    在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old ),新生代默认占总空间的 1/3,老年代默认占 2/3。

    新生代有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1。

    新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。

    老年代的垃圾回收(又称Major GC)通常使用“标记-清理”或“标记-整理”算法。

    再描述它们之间转化流程:

    对象优先在Eden分配。当 eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。

    在 Eden 区执行了第一次 GC 之后,存活的对象会被移动到其中一个 Survivor 分区;

    Eden 区再次 GC,这时会采用复制算法,将 Eden 和 from 区一起清理,存活的对象会被复制到 to 区;

    移动一次,对象年龄加 1,对象年龄大于一定阀值会直接移动到老年代。GC年龄的阀值可以通过参数 -XX:MaxTenuringThreshold 设置,默认为 15;

    动态对象年龄判定:Survivor 区相同年龄所有对象大小的总和 > (Survivor 区内存大小 * 这个目标使用率)时,大于或等于该年龄的对象直接进入老年代。其中这个使用率通过 -XX:TargetSurvivorRatio 指定,默认为 50%;

    Survivor 区内存不足会发生担保分配,超过指定大小的对象可以直接进入老年代。

    大对象直接进入老年代,大对象就是需要大量连续内存空间的对象(比如:字符串、数组),为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。

    老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 –包括年轻代和老年代

    Android包体积优化方案

    1.对dex文件进行优化,减少无用代码,减少java,aidl文件

    2.对资源文件进行优化

    3.添加混淆

    事件分发机制

    用户触摸屏幕的时候,会触发Touch事件。主要发生的Touch事件有如下四种:

    MotionEvent.ACTION_DOWN:按下View(所有事件的开始)

    MotionEvent.ACTION_MOVE:滑动View

    MotionEvent.ACTION_CANCEL:非人为原因结束本次事件

    MotionEvent.ACTION_UP:抬起View(与DOWN对应)

    即当一个MotionEvent 产生后,系统需要把这个事件传递给一个具体的 View 去处理,

    事件分发的本质是将点击事件(MotionEvent)向某个View进行传递并最终得到处理

    即当一个点击事件发生后,系统需要将这个事件传递给一个具体的View去处理。这个事件传递的过程就是分发过程。

    一个点击事件产生后,传递顺序是:Activity(Window) -> ViewGroup -> View

    事件分发过程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三个方法协助完成

    其中:

    super:调用父类方法

    true:消费事件,即事件不继续往下传递

    false:不消费事件,事件也不继续往下传递 / 交由给父控件onTouchEvent()处理

    方法详细介绍 事件分发机制详细流程

    默认情况

    即不对控件里的方法(dispatchTouchEvent()、onTouchEvent()、onInterceptTouchEvent())进行重写或更改返回值

    那么调用的是这3个方法的默认实现:调用父类的方法

    事件传递情况:(如图下所示)

    从Activity A---->ViewGroup B--->View C,从上往下调用dispatchTouchEvent()

    再由View C--->ViewGroup B --->Activity A,从下往上调用onTouchEvent()

    说说对称加密和非对称加密

    简介: 对称加密: 加密和解密的秘钥使用的是同一个.

    非对称加密: 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。

    对称加密算法: 密钥较短,破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,且对计算机性能要求也没有那么高.

    优点: 算法公开、计算量小、加密速度快、加密效率高

    缺点: 在数据传送前,发送方和接收方必须商定好秘钥,然后 使双方都能保存好秘钥。其次如果一方的秘钥被泄露,那么加密信息也就不安全了。另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一秘钥,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担。 常见的对称加密算法有: DES、3DES、Blowfish、IDEA、RC4、RC5、RC6 和 AES

    非对称加密算法: 公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

    非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。甲方只能用其专用密钥解密由其公用密钥加密后的任何信息。

    优点: 安全

    缺点: 速度较慢 常见的非对称加密算法有: RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)

    锁有哪些

    从线程是否需要对资源加锁可以分为 悲观锁 和 乐观锁

    从资源已被锁定,线程是否阻塞可以分为 自旋锁

    从多个线程并发访问资源,也就是 Synchronized 可以分为 无锁、偏向锁、 轻量级锁 和 重量级锁

    从锁的公平性进行区分,可以分为公平锁 和 非公平锁

    从根据锁是否重复获取可以分为 可重入锁 和 不可重入锁

    从那个多个线程能否获取同一把锁分为 共享锁 和 排他锁

    一文足以了解什么是 Java 中的锁 (baidu.com)

    相关文章

      网友评论

          本文标题:2021-08-20

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