集合-锁-JDK-反射

❤4、集合
1、Map和ConcurrentHashMap的区别?

2、hashMap内部具体如何实现的?

hashmap是一个key-value键值对的数据结构,从结构上来讲在jdk1.8之前是用数组加链表的方式实现,jdk1.8加了红黑树,hashmap数组的默认初始长度是16,hashmap数组只允许一个key为null,允许多个value为null
hashmap的内部实现,hashmap是使用数组+链表+红黑树的形式实现的,其中数组是一个一个Node[]数组,我们叫他hash桶数组,它上面存放的是key-value键值对的节点。HashMap是用hash表来存储的,在hashmap里为解决hash冲突,使用链地址法,简单来说就是数组加链表的形式来解决,当数据被hash后,得到数组下标,把数据放在对应下表的链表中。
然后再说一下hashmap的方法实现
put方法,put方法的第一步,就是计算出要put元素在hash桶数组中的索引位置,得到索引位置需要三步,去put元素key的hashcode值,高位运算,取模运算,高位运算就是用第一步得到的值h,用h的高16位和低16位进行异或操作,第三步为了使hash桶数组元素分布更均匀,采用取模运算,取模运算就是用第二步得到的值和hash桶数组长度-1的值取与。这样得到的结果和传统取模运算结果一致,而且效率比取模运算高
jdk1.8中put方法的具体步骤,先判断hashmap是否为空,为空的话扩容,不为空计算出key的hash值i,然后看table[i]是否为空,为空就直接插入,不为空判断当前位置的key和table[i]是否相同,相同就覆盖,不相同就查看table[i]是否是红黑树节点,如果是的话就用红黑树直接插入键值对,如果不是开始遍历链表插入,如果遇到重复值就覆盖,否则直接插入,如果链表长度大于8,转为红黑树结构,执行完成后看size是否大于阈值threshold,大于就扩容,否则直接结束
get方法就是计算出要获取元素的hash值,去对应位置取即可。
扩容机制,hashmap的扩容中主要进行两部,第一步把数组长度变为原来的两倍,第二部把旧数组的元素重新计算hash插入到新数组中,在jdk1.8时,不用重新计算hash,只用看看原来的hash值新增的一位是零还是1,如果是1这个元素在新数组中的位置,是原数组的位置加原数组长度,如果是零就插入到原数组中。扩容过程第二部一个非常重要的方法是transfer方法,采用头插法,把旧数组的元素插入到新数组中。
3.hashmap大小为什么是2的幂次方
在计算插入元素在hash桶数组的索引时第三步,为了使元素分布的更加均匀,用取模操作,但是传统取模操作效率低,然后优化成h&(length-1),设置成2幂次方,是因为2的幂次方-1后的值每一位上都是1,然后与第二步计算出的h值与的时候,最终的结果只和key的hashcode值本身有关,这样不会造成空间浪费并且分布均匀,如果不是2的幂次方
如果length不为2的幂,比如15。那么length-1的2进制就会变成1110。在h为随机数的情况下,和1110做&操作。尾数永远为0。那么0001、1001、1101等尾数为1的位置就永远不可能被entry占用。这样会造成浪费,不随机等问题。

3、如果hashMap的key是一个自定义的类,怎么办?

如果hashMap的key是一个自定义的类,必须重写该类的hashcode()方法和equals()方法
HashMap中,如果要比较key是否相等,要同时使用这两个函数!因为自定义的类的hashcode()方法继承于Object类,其hashcode码为默认的内存地 址,这样即便有相同含义的两个对象,比较也是不相等的,equals()比较的是内存地址是否相等。例如,
Student st1 = new Student(“wei”,“man”);
Student st2 = new Student(“wei”,“man”);
正常理解这两个对象再存入到hashMap中应该是相等的,但如果你不重写 hashcode()方法的话,比较是其地址,不相等!

  HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),
  若相等则认为他们是相等 的。若equals()不相等则认为他们不相等。如果只重写hashcode()不
  重写equals()方法,当比较equals()时只是看他们是否为 同一对象(即进行内存地址的比较),
  所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet
  判断加入元素 是否相等。

5、HashMap底层,负载因子,为啥是2^n?

HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法; 这个算法实际就是取模,hash%length,计算机中直接求余效率不如位移运算,源码中做了优化hash&(length-1), hash%length==hash&(length-1)的前提是length是2的n次方; 为什么这样能均匀分布减少碰撞呢?2的n次方实际就是1后面n个0,2的n次方-1 实际就是n个1; 例如长度为9时候,3&(9-1)=0 2&(9-1)=0 ,都在0上,碰撞了; 例如长度为8时候,3&(8-1)=3 2&(8-1)=2 ,不同位置上,不碰撞; 其实就是按位“与”的时候,每一位都能 &1 ,也就是和1111……1111111进行与运算

6、ConcurrentHashMap锁加在了哪些地方?

7、TreeMap底层,红黑树原理?

8、concurrenthashmap有啥优势,1.7,1.8区别?

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

9、ArrayList是否会越界?

会的,底层是数组实现,是数组就一定会有越界的问题存在。

10、什么是TreeMap?

11、ConcurrentHashMap的原理是什么?

12、Java集合类框架的基本接口有哪些?

总共有两大接口:Collection 和Map ,一个元素集合,一个是键值对集合; 其中List和Set接口继承了Collection接口,一个是有序元素集合,一个是无序元素集合; 而ArrayList和 LinkedList 实现了List接口,HashSet实现了Set接口,这几个都比较常用; HashMap 和HashTable实现了Map接口,并且HashTable是线程安全的,但是HashMap性能更好;

13、为什么集合类没有实现Cloneable和Serializable接口?

克隆和序列化是跟具体的实现有关的,所以应该跟集合的实现类来决定如何实现克隆和序列化

14、什么是迭代器?

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
  Java中的Iterator功能比较简单,并且只能单向移动:
  (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
  (2) 使用next()获得序列中的下一个元素。
  (3) 使用hasNext()检查序列中是否还有元素。
  (4) 使用remove()将迭代器新返回的元素删除。
  Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
  
15、Iterator和ListIterator的区别是什么?

两者都可以用来遍历集合,iterator可以遍历所有集合,listiterator可以向前遍历也可以向后遍历,iterator只可以向后遍历,listiterator可以定位当前索引位置。而iterator不能

16、快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

  • 一:快速失败(fail—fast)
    在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent
    Modification Exception。
    原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
    注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
    场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。
  • 二:安全失败(fail—safe)
    采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
    原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent
    Modification Exception。
    缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
    场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

17、HashMap和Hashtable有什么区别?

HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:
HashMap允许键和值是null,而Hashtable不允许键或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。另一方面,Hashtable提供了对键的列举(Enumeration)。
一般认为Hashtable是一个遗留的类。

18、ArrayList和LinkedList有什么区别?

ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
理论上来说在做新增和删除操作add和remove时,LinedList比较占优势,因为ArrayList要移动数据。

19、ArrayList,Vector,LinkedList的存储性能和特性是什么?

ArrayList是实现了基于动态数组的数据结构,LinkedList基于双线链表的数据结构。

ArrayList可以随机定位对于新增和删除操作add和remove,LinedList比较占优势

具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。

Vector与ArrayList唯一的区别是,Vector是线程安全的,即它的大部分方法都包含有关键字synchronized,因此,若对于单一线程的应用来说,最好使用ArrayList代替Vector,因为这样效率会快很多(类似的情况有StringBuffer线程安全的与StringBuilder线程不安全的);而在多线程程序中,为了保证数据的同步和一致性,可以使用Vector代替ArrayList实现同样的功能。

20、Collection 和 Collections的区别。

Collection是集合类的上级接口,继承于它的接口主要有Set和List。
Collections是针对集合类的一个帮助类,它提供了一系列静态方法实现了对各种集合的排序,搜索和线程安全等操作。

21、、List、Map、Set三个接口存取元素时,各有什么特点?

  • 存放时:
    1.List以特定的索引(有顺序的存放)来存放元素,可以有重复的元素
    2.Set存放元素是无序的,而且不可重复
    3.Map保存键值对的映射,映射关系可以是一对一(键值)或者多对一,需要注意到的是:键无序不可重复,值可以重复

  • 取出时:

    (1)List取出元素for循环,foreach循环,Iterator迭代器迭代

    (2)Set取出元素foreach循环,Iterator迭代器迭代

    (3)Map取出元素需转换为Set,然后进行Iterator迭代器迭代,或转换为Entry对象进行Iterator迭代器迭代

❤5、线程

1、多线程中的i++线程安全吗?为什么?

  • i++不是原子操作,也就是说,它不是单独一条指令,而是3条指令(3条汇编指令):
  • 1、从内存中把i的值取出来放到CPU的寄存器中 2、CPU寄存器的值+1 3、把CPU寄存器的值写回内存
  • 由于线程共享栈区,不共享堆区和全局区,所以当且仅当 i 位于栈上是安全的,反之不安全(++i也同理).
    因为如果是全局变量的话,同一进程中的不同线程都有可能访问到。对于读值,+1,写值这三步操作,在这三步任何之间都可能会有CPU调度产生,造成i的值被修改,造成脏读脏写。
  • volatile不能解决这个线程安全问题。因为volatile只能保证可见性,不能保证原子性。

2、如何线程安全的实现一个计数器?

  • AtomicInteger
  • sychronized
  • Lock

3、多线程同步的方法

  • 第一种:使用synchronized关键字修饰
  • 第二种:wait()方法和notify()方法或notifyAll()方法
  • 第三种:Lock

4、介绍一下生产者消费者模式?

是一种平衡机制,生产者和消费者相互协作,有效的避免资源浪费,可以提高效率

5、线程然后线程创建有很大开销,怎么优化?

使用线程池

6、线程池运行流程,参数,策略
在这里插入图片描述
7、讲一下AQS吧。

AbstractQueuedSynchronized,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它
AQS是一个并发包的基础组件,用来实现各种锁,各种同步组件。它包含了state变量,加锁线程,等待队列并发中的核心组件。

8、创建线程的方法,哪个更好,为什么?

  • 1,继承Thread类,重写run方法;
  • 2,实现Runnable接口,重写run方法,但是比继承Thread类好用,实现接口还可以继承类,避免了单继承带来的局限性;
  • 3,实现callable接口,重写call方法,有返回值。
  • 4,使用实现了Executor接口的ThreadPoolExecutor来创建线程池。
  • 10、Java中有几种线程池?
  • newsingleThreadExecutor:单线程的线程池,处理完一个任务接着下一个,若异常则起一个新的线程
  • newFixedThreadPool:指定数目的线程池,如果多于这个数目则加入缓存队列
  • newcachedThreadPool:不限数目的线程池,完全依赖于JVM能创建的线程数,可能出现内存不足
  • 自定义线程池:通过修改五大核心参数来控制;
    在这里插入图片描述
    11、线程池有什么好处?

降低资源消耗,提高响应速度,提高线程的可管理性

12、cyclicbarrier和countdownlatch的区别

CountdownLatch阻塞主线程,等所有子线程完结了再继续下去。Syslicbarrier阻塞一组线程,直至某个状态之后再全部同时执行,并且所有线程都被释放后,还能通过reset来重用。

15、概括的解释下线程的几种可用状态。
1.新建状态(New):
当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
2.就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
3.运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4. 阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件.
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5. 死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.
在这里插入图片描述

18、在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?

在 java 虚拟机中, 每个对象( Object 和 class )通过某种逻辑关联监视器,每个监视器和一个对象引用相关联, 为了实现监视器的互斥功能, 每个对象都关联着一把锁.
一旦方法或者代码块被 synchronized 修饰, 那么这个部分就放入了监视器的监视区域, 确保一次只能有一个线程执行该部分的代码, 线程在获取锁之前不允许执行该部分的代码

另外 java 还提供了显式监视器( Lock )和隐式监视器( synchronized )两种锁方案

19、sleep() 和 wait() 有什么区别?

  • 1.sleep方法没有释放锁,但是wait方法释放了锁,使得其他线程可以使用同步控制块
  • 2.sleep可以在任何地方使用,wait notify notifyall只能使用在同步控制块中
  • 3.sleep必须捕获异常,其他不需要

20、同步和异步有何异同,在什么情况下分别使用他们?举例说明。

同步是安全的,异步是不安全的。同步是发送一个请求,等待返回,然后在发送一个请求。异步是发送一个请求,不用等待,随时可发送下一个请求

21、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。使用内部类实现线程,对j增减的时候没有考虑顺序问题。

public class Main{
    private int j=0;//定义变量j
    public synchronized add(){
        j++;//定义同步方法每次只有一个线程对j进行j++操作
    }
    public synchronized dec(){
      j--;//定义同步方法每次只有一个线程对j进行j--操作
    }
   public static void main(String[] args){
         for(int i=0;i<2;i++){
              new Thead(new Runnable(){//使用匿名内部类进行线程的创建,重写run()方法,调用add()方法
            public void run(){
           while(true){
                 add();
           }
         }
         }).start();
         new Thead(new Runnable(){//使用匿名内部类进行线程的创建,重写run()方法,调用dec()方法
           public void run(){
              while(true){
                dec();
           }
          }
        }).start();
        }
   }
}

**

23、请说出你所知道的线程同步的方法

  • 1 同步方法
  • 2 同步块
  • 3 wait 和 notify
  • 4 volatile
  • 5 Lock : ReentrantLock
  • 6.局部变量比如ThreadLocal
  • 7 blockqueue

25、 stop()和suspend()方法为何不推荐使用?

stop会导致不安全,为啥呢,如果在同步块执行一半时,stop来了,后面还没执行完呢,锁没了,线程退出了,别的线程又可以操作你的数据了,所以就是线程不安全了。 suspend会导致死锁,因为挂起后,是不释放锁的,别人也就阻塞着,如果没人唤醒,那就一直死锁。

26、线程的sleep()方法和yield()方法有什么区别?

sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会
线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态
sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常

27、当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

32、如何保证线程安全?

1)synchronized关键字
2)lock接口
3)volatile+CAS【单纯的volatile是轻量级的同步机制保证可见性但是不具备原子性所以要配合CAS来实现线程安全】
4)atomic原子类【比较冷门,大家可以去看看】

❤6、锁

1、讲一下非公平锁和公平锁在reetrantlock里的实现。

非公平锁: 当线程争夺锁的过程中,会先进行一次CAS尝试获取锁,若失败,则进入acquire(1)函数,进行一次tryAcquire再次尝试获取锁,若再次失败,那么就通过addWaiter将当前线程封装成node结点加入到Sync队列,这时候该线程只能乖乖等前面的线程执行完再轮到自己了。
公平锁: 当线程在获取锁的时候,会先判断Sync队列中是否有在等待获取资源的线程。若没有,则尝试获取锁,若有,那么就那么就通过addWaiter将当前线程封装成node结点加入到Sync队列中。

2、讲一下synchronized,可重入怎么实现

对象监视器。会在对象头部有个区域,专门记录锁信息。包括持有锁的线程,锁的计数器,锁的状态这些。 线程在尝试获取对象锁时,先看看锁计数器是不是为0,为零就说明锁还在,于是获取锁,计数器变成1,并记录下持有锁的线程,当有线程再来请求同步方法时,先看看是不是当前持有锁的线程,是的,那就直接访问,锁计数器+1,如果不是,对不起,你阻塞吧。当退出同步块时,计数器-1,变成0时,释放锁。

3、锁和同步的区别。

Lock是一个接口,而synchronized是关键字。
synchronized会自动释放锁,而Lock必须手动释放锁。
Lock可以让等待锁的线程响应中断,而synchronized不会,线程会一直等待下去。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。
Lock能提高多个线程读操作的效率。
synchronized能锁住类、方法和代码块,而Lock是块范围内的

4、什么是死锁(deadlock)?

  • 所谓死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法
  • 向前推进。死锁产生的4个必要条件: 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某
    资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
    不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
    请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源
    已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
    循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求

5、如何确保N个线程可以访问N个资源同时又不导致死锁?

多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环,这四个条件缺一不可,只要破坏了其中一个条件就可以破坏死锁,其中最简单的方法就是线程都是以同样的顺序加锁和释放锁,也就是破坏了第四个条件。

❤7、JDK

1、Java中的LongAdder和AtomicLong的区别

网上找的: AtomicLong是作用是对长整形进行原子操作,显而易见,在java1.8中新加入了一个新的原子类LongAdder,该类也可以保证Long类型操作的原子性,相对于AtomicLong,LongAdder有着更高的性能和更好的表现,可以完全替代AtomicLong的来进行原子操作。 在32位操作系统中,64位的long 和 double 变量由于会被JVM当作两个分离的32位来进行操作,所以不具有原子性。而使用AtomicLong能让long的操作保持原子型。 AtomicLong的实现方式是内部有个value 变量,当多线程并发自增,自减时,均通过cas 指令从机器指令级别操作保证并发的原子性。 唯一会制约AtomicLong高效的原因是高并发,高并发意味着CAS的失败几率更高, 重试次数更多,越多线程重试,CAS失败几率又越高,变成恶性循环,AtomicLong效率降低。 那怎么解决? LongAdder给了我们一个非常容易想到的解决方案: 减少并发,将单一value的更新压力分担到多个value中去,降低单个value的 “热度”,分段更新!!! 这样,线程数再多也会分担到多个value上去更新,只需要增加value就可以降低 value的 “热度” AtomicLong中的 恶性循环不就解决了吗? cells 就是这个 “段” cell中的value 就是存放更新值的, 这样,当我需要总数时,把cells 中的value都累加一下不就可以了么!! 当然,聪明之处远远不仅仅这里,在看看add方法中的代码,casBase方法可不可以不要,直接分段更新,上来就计算 索引位置,然后更新value? 答案是不好,不是不行,因为,casBase操作等价于AtomicLong中的cas操作,要知道,LongAdder这样的处理方式是有坏处的,分段操作必然带来空间上的浪费,可以空间换时间,但是,能不换就不换,看空间时间都节约~! 所以,casBase操作保证了在低并发时,不会立即进入分支做分段更新操作,因为低并发时,casBase操作基本都会成功,只有并发高到一定程度了,才会进入分支,所以,Doug Lead对该类的说明是: 低并发时LongAdder和AtomicLong性能差不多,高并发时LongAdder更高效! 但是,Doung Lea 还是没这么简单,聪明之处还没有结束…… 如此,retryUpdate中做了什么事,也基本略知一二了,因为cell中的value都更新失败(说明该索引到这个cell的线程也很多,并发也很高时) 或者cells数组为空时才会调用retryUpdate, 因此,retryUpdate里面应该会做两件事: 1. 扩容,将cells数组扩大,降低每个cell的并发量,同样,这也意味着cells数组的rehash动作。 2. 给空的cells变量赋一个新的Cell数组。 LongAdder确实用了很多心思减少并发量,并且,每一步都是在”没有更好的办法“的时候才会选择更大开销的操作,从而尽可能的用最最简单的办法去完成操作。追求简单,但是绝对不粗暴。 AtomicLong可不可以废掉? 我的想法是可以废掉了,因为,虽然LongAdder在空间上占用略大,但是,它的性能已经足以说明一切了,无论是从节约空的角度还是执行效率上,AtomicLong基本没有优势了。

2、JDK和JRE的区别是什么?

dk是java语言开发工具包,jre是java运行环境,jvm是虚拟机,执行.class文件。jdk包含jre,jre包含jvm

**❤8、反射

1、反射的实现与作用**

  • 反射的用途 1. 各种开发框架中,如Spring。 2. 创建数据库链接时,如Class
    class=Class.forName(“com.java.dbtest.TestConnection”)
    3.在IDE中对象.属性,这时候会用到反射
  • Java反射机制主要提供了以下功能:
    在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值