面试问题,持续更新...

1.<label></label>标签在IE下无法使用,只需要在显示的标签后加上disabled="disabled"就好了;
2.被Native修饰的方法就是一个java调用非Java代码的接口.一个Native Method:该方法的实现由非java语言实现,并不会提供实现类;
3.RestFul其实是一套架构约束和规则;一套标准,主要是通过url对资源的定位风格!比如http的post和get方法,方法即要到达的资源或者操作!通过/来表示层级结构,通过?过滤资源
4.Redis加锁的方式
    1.INCR key不存在的话 key值被初始化为0,然后再INCR操作,然后就是1,如果返回大于1,说明在使用中;
    2.SETNX  key不存在 讲key设置为value.如果key存在 则不做任何动作
    3.SET 设置key 成功就枷锁.相同key就失败 
    问题:失败要循环请求, 等待几毫秒再执行 否则会发生抢锁现象;在删除锁的时候判断value值是否和自己的一样;
    SETNX +GETSET 防止高并发  返回的是value 如果设置的是当前时间 
    while(jedis.setnx(lock, now+超时时间)==0){
        if(now>jedis.get(lock) && now>jedis.getset(lock, now+超时时间)){
            break;
        }else{
            Thread.sleep(300);
        }
    }
    执行业务代码;
    jedis.del(lock);
5.使用ThreadPoolTaskExecutor创建线程池 可以通过配置创建最合适的线程池;
    线程核心池大小
    线程池最大线程数
    空闲线程等待时间
    时间单位
    用来存储等待任务的队列
    线程工厂
    拒绝策略
    ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
    LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
    PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
    DelayQueue: 一个使用优先级队列实现的无界阻塞队列。
    SynchronousQueue: 一个不存储元素的阻塞队列。
    LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
    LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。
    
    ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出
    RejectedExecutionException异常。 (默认)
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务


6.Spring事务回滚的异常默认只有碰到运行时异常才能回滚;
使用的dbcp连接池1.4版本,数据源使用的是BasicDataSource;
使用jdbcTemlate进行注入,Spring4.x之后,使用NamedParameterJdbcOperations进行兼容,可以使用所有的原生template方法  
7.注解的原理:
      运行时通过反射处理,编译时通过注解处理器处理 权限注解和枚举注解和转换注解等
    使用:使用fastjson进行json脱敏转换的时候,为每个注解定义了一个过滤器,字段可以直接get方法获取到枚举;
         通过在mvc中定义拦截器进行权限控制
  


Serverless:服务器对用户来说是透明的
    1. 程序员编写完成业务的函数代码
    2. 上传到支持Serverless的平台,设定触发的规则。
    3. 请求到来,Serverless平台根据触发规则加载函数,创建函数实例,运行
    4. 如果请求比较多,会进行实例的扩展,如果请求较少,就进行实例的收缩。
    5. 如果无人访问,卸载函数实例。
缺点:
    1.用户的会话状态肯定是无法保持的,像session sticky 这样的功能就别想了。
    2.函数无法做本地的持久化,没法访问本地硬盘的任何东西


Metaspce触发的GC都是Full GC。

可达性分析法:GC Root 其实就是正在工作的对象
①虚拟机栈(栈桢中的本地变量表)中的引用的对象,就是平时所指的java对象,存放在堆中。
②方法区中的类静态属性引用的对象,一般指被static修饰引用的对象,加载类的时候就加载到内存中。
③方法区中的常量引用的对象,
④本地方法栈中JNI(native方法)引用的对象
当一个对象都不被这些引用 则会被视为垃圾对象

unsafe类 

dubbo:
    本地暴露的url是以injvm开头的;
    远程暴露是以registry开头的

    ProtocolFilterWrapper
    ProtocolListenerWrapper


    protocol 通过注解@Adaptive加载配置文件中的类生成的

    AdaptiveClassCodeGenerator

    ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); 
    SPI技术 将要调用的默认类放到了配置中

Dubbo 处理服务暴露的关键就在 Invoker 转换到 Exporter 的过程

    ListenerExporterWrapper


dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能通信吗?
我们看到zookeeper的信息会缓存到本地作为一个缓存文件,并且转换成properties对象方便使用.


CuratorZookeeperClient  zk的客户端


eventpulishRunlisteners    

命令式编程:关注计算机的执行步骤,一步一步告诉计算机做什么;
声明式编程:类似于sql,只告诉计算机要做什么
    不需要创建变量存储数据
    不包含循环控制代码

函数式编程:使用函数编程的一种编程范式;
    没有变量改变,没有打印动作,没有写入动作,没有抛出异常;
    就是编写非故意的副作用的程序
    函数式编程就是编写非有意副作用的程序,如果需要副作用尽可能的延迟副作用发生的时机。如果既有返回值又有副作用,这种程序就不是函数式。

    函数式接口:
        被@FunctionalInterface注释的接口 或者满足@FunctionalInterface约束的    
        约束:
            接口只有一个抽象方法.可以复写Object里的方法

        创建对象相当于定义了方法体;
        只要符合接口里定义方法的结构,都可以当做对象来创建


柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
感觉类似于构造函数的重载并且在构造里调用构造,差别只是参数的多少;

Java 1.8 function包
    通用方法 andthen和compose方法,都支持传入函数
        andThen 先函数 再本身
        compose 先本身 再函数
    1.Consumer
        Consumer<T> 接受对象 处理对象 但是不返回值
        同一对象多样化的重复操作可以封装,比如遍历器Iterable的foreach方法,就支持传入一个函数,旨在解放单一处理逻辑;    
      BiConsumer
          BiConsumer<T, U>  接受T 和U对象 不返回值
        
        void accept();
          andThen

    2.Predicate
        Predicate<T> 接收对象并返回boolean;
        同一对象的条件处理逻辑的封装;
        其中包括是,非,和,或四种逻辑
      BiPredicate
          BiPredicate<T, U> 接受对象,返回布尔值
    3.Function
        Function<T,R> 接收对象,返回另一对象
        函数思想,转换逻辑可以提出来,单一的转换逻辑
        BiFunction<T,U,R> 接收T,U  返回R
    4.Supplier
          Supplier<T>    提供T对象 不接收值
          封装工厂创建对象的逻辑
    5.UnaryOperator
        UnaryOperator<T> 继承自Function<T,R>
        一元函数的思想,将同类转换逻辑提取出来,解耦合


Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体

If there’s one rule in programming, it’s this: there will always be trade-offs.


单向散列函数:
    单向散列函数,又称单向Hash函数、杂凑函数,就是把任意长的输入消息串变化成固定长的输出串且由输出串难以得到输入串的一种函数。这个输出串称为该消息的散列值。一般用于产生消息摘要,密钥加密等.
    一般有:
        MD4,MD5,SHA-1、SHA-256、SHA-384、SHA-512,PIPEMD-160(比特币),SHA-3

BitSet:
    可以按位存储,一个字节占8位,值只有0或1,内部维护一个long数组,初始化只有一个long 空间段,默认一次扩充64位;
    场景:海量数据去重、排序、压缩存储
        布隆过滤器:
            空间效率高的概率型数据结构,用来检查一个元素是否在一个集合中。
            对于一个元素检测是否存在的调用,BloomFilter会告诉调用者两个结果之一:可能存在或者一定不存在。
    优点:
        按位存储,空间小
        丰富的API操作
    缺点:
        线程不安全
        内部动态扩展,若数据稀疏,占用内存大
        但是,哈希函数发生冲突的概率比较高,若要降低冲突概率到1%,就要将BitSet的长度设置为URL个数的100倍。
    为什么用long:
    如果是long数组存储,我们可以每次读入64个bit,而int数组存储时,只能每次读入32个bit。


    若题目要求只能遍历一次链表,那又当如何解决问题?

可以采取建立两个指针,一个指针一次遍历两个节点,另一个节点一次遍历一个节点,当快指针遍历到空节点时,慢指针指向的位置为链表的中间位置,这种解决问题的方法称为快慢指针方法。

锁和监视器:
    目前理解:获得锁是获得了对象的锁,Lock锁的Java代码实现层面上是通过cas进行变量的设置,而sychronazed的是对象锁,是编译层面的实现,其实是在对象头中设置获得锁的线程的信息;而对象即为监视器,
            编译之后,需要进行获取和释放的过程,获取即为信息设置到对象头,释放的时候,除非有竞争,否则不会主动去消除印记,此处涉及锁膨胀
Java的锁膨胀
    偏向锁:
    当一个线程要获取该对象的锁的时候,发现只有一个线程,所以获取偏向锁,在锁的对象头记录线程ID,下次再申请锁的时候可以直接获取到锁;
如果不是此线程,当标志位位0,说明发生了竞争,已经变为轻量级锁,使用CAS尝试获取锁;如果标志位为1,还是偏向锁,尝试将对象头的线程记录改为新线程;
    轻量级锁:
    CAS失败,产生竞争之后的锁,JVM在当前线程的栈帧中创建用于存储锁记录的空间,然后把MarkWord放进去,生成Owner的指针指向那个加锁对象,同时尝试把对象头的MarkWord变成一个指向锁记录的指针(新的MarkWord).
如果尝试失败,继续CAS,如果还不能获得,说明竞争升级,膨胀为重量级锁;
    解锁的时候,尝试将MarkWord回退到原来的记录,成功则表示没竞争,如果失败,则表示有其他线程尝试修改过,需要改回去再进行线程唤醒;
    重量级锁:
    直接阻塞,互斥锁,等待其他线程释放之后唤醒再竞争;

Java的锁降级
    共享锁(S锁)又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。    
    排他锁(X锁)又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。

    线程A获取了写锁,更新了数据,然后继续读取的时候,会发生锁降级,先获取读锁,然后再释放写锁,在写锁中获取到了读锁;
    如果说A获取写锁之后,写完直接放弃写锁,再去获取读锁,如果没有竞争到锁,线程B获取到写锁之后,线程A就得等待再去竞争,竞争是需要消耗资源的,其实是相当于减少了一次竞争;

三种服务发现模式:
    1.传统集中式代理:
        在服务生产者和消费者之间,代理作为独立一层集中部署,由独立团队负责治理和运维。
        常用的有F5和Nginx等,有个问题是各模块的缓存刷新问题,每个节点刷新自己的缓存,如果下次请求不在这个节点,会出问题
        
        优点:运维简单,集中治理,与语言无关
        缺点:配置周期长,性能开销,单点
    2.客户端嵌入模式:
        代理以库的形式嵌入到程序中,需要独立的负载中心组件配合.Dubbo是典型的例子

        优点:无单点,性能好
        缺点:客户端复杂,多语言麻烦
    3.ServiceMash主机独立进程代理:
        代理作为独立进程部署在每一个主机上由本机所有进程公用,也需要注册中心

        优点:折中方案
        缺点:运维复杂,服务过多的话

索引原理:
    如果新建表没有主键,不加索引,在磁盘上的排列顺序是一行一行但是无序的状态,这才叫表,即使一个主键,也会将表转换成树状结构;
    如果单纯的表状排列,查询数据是需要扫描全表的,对系统的内存和CPU都有很大的开销,但是转换成树状结构之后,查找次数为大大降低(log 记录数/树的分叉数),但是既然是平衡树,每次增删改都会破坏树的平衡,数据库操作之前,DBMS必须重新梳理树的结构,越大越难梳理;
所以查询和增删改需要追求一个平衡状态,可以读写分离,可以冷热分离,这样才能追求最大效率;
    聚集索引:主键
    非聚集索引:唯一索引,非唯一索引,全文索引
        如果添加索引之后,字段中的数据会被复制出来一份用于生成索引,所以会增大表的体积,占用磁盘存储空间;
    非聚集索引和聚集索引的区别在于, 通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据;
    如果说所有字段都被查询条件覆盖,则会直接找到记录,不用通过主键
    两个字段建立复合索引和单独建立索引比较:A1,A2,只有按顺序联合搜索时,才是复合索引快
二叉树:
    空二叉树,一个节点的,左子树,右子树,完全二叉树;
    (1)完全二叉树——满二叉树从右往左删除若干个节点,得到的都是完全二叉树。
    (2)满二叉树——除叶子节点外,所有的节点都达到了最大的;
    (3)平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
辨析
    二叉树不是树的一种特殊情形,尽管其与树有许多相似之处,但树和二叉树有两个主要差别:
    1. 树中结点的最大度数没有限制,而二叉树结点的最大度数为2;
    2. 树的结点无左、右之分,而二叉树的结点有左、右之分。


SpringBoot启动之后回调函数
    CommandLineRunner或者ApplicationRunner接口的实现类都会被执行,函数式接口;


spring只扫描事务相关bean(如@Service和@Dao)等
springmvc只负责扫描@Controller即可
这样,bean不被重复初始化。并且不会出现事务问题。
ps:如果springmvc扫描了@Service和@Dao会出现事务失效

Spring扫描Bean是为了开启事务,而MVC如果再扫描一次的话 会覆盖前一次扫描出的配置,所以事务会失效,如果有定时任务的话,定时任务会执行两次,相当与将两次任务加入到了定时队列中;
虽然Bean是单例的,但是任务确实可以多次添加的;

Cloneable 接口的作用

实现接口 重写clone方法 才能复制  否则会报异常ClassNotSupportedException
 native修饰的方法都是空的方法,但是这些方法都是有实现体的(这里也就间接说明了native关键字不能与abstract同时使用。因为abstract修饰的方法与java的接口中的方法类似,他显式的说明了修饰的方法,在当前是没有实现体的,abstract的方法的实现体都由子类重写),只不过native方法调用的实现体,都是非java代码编写的(例如:调用的是在jvm中编写的C的接口),每一个native方法在jvm中都有一个同名的实现体,native方法在逻辑上的判断都是由实现体实现的,另外这种native修饰的方法对返回类型,异常控制等都没有约束。 


首要有必要了解一下java.exe 如何寻找JRE库的:

当在控制台执行java.exe,操作系统寻找JRE的方式如下:
1) 先找当前目录下有没有JRE
2)再找父目录下有没有JRE
3)接着在PATH路径中找JRE
4)注册表HKEY_LOCAL_MACHINESOFTWAREJavaSoftJava Runtime Environment 查看CurrentVersion的键值指向哪个JRE

2.JRE类库查找

JRE自带的基础类库主要是JRElib t.jar这个文件,包括了Java2平台标准版的所有类库。和JRE的版本一致。

JRE中由ClassLoader负责查找和加载程序引用到的类库,基础类库ClassLoader会到rt.jar中自动加载,其它的类库,ClassLoader在环境变量CLASSPATH指定的路径中搜索,按照先来先到的原则,放在CLASSPATH前面的类库先被搜到,Java程序启动之前建议先把PATH和CLASSPATH环境变量设好,OS通过PATH来找JRE,确定基础类库rt.jar的位置,JRE的 ClassLoader通过CLASSPATH找其它类库。但有时候会出现这样的情况,希望替换基础类库中的类库,那么也可以简单的通过 -Djava.endrosed.path=...参数传递给java.exe,于是ClassLoader会先于基础类库使用 java.endrosed.path参数指定路径的类库。因此Java的版本管理是非常简单有效的,也许很原始,不过很好用,简单就不容易出错。

3.Java的虚拟机启动和加载类库

在Console执行java.exe xxx命令以后,如前所述的寻找JRE,OS找到JRE目录,根据java.exe的传递参数,选择加载Server版的jvm.dll还是Client 版的jvm.dll,然后加载jvm.dll,把控制权交给jvm.dll。

接下来,jvm.dll进行初始化,分配内存等等动作,然后在CLASSPATH路径中寻找class,找到class以后,寻找class中的程序入口点Main函数,然后从Main函数执行程序,在执行过程中,使用ClassLoader动态加载一系列引用到的类。当调用到native方法时,jvm.dll告诉OS在JRE in目录下寻找某某DLL文件,调入内存,于是实现了JNI调用。


从上面的GC日志,我们看到了Full GC前后,Metaspace的使用变化是从137752K->71671K,其实你们如果用的oracle官方的JDK,看到的会是137752K->137752K,也就是并没有发生变化,看起来好像Metaspace并没有被回收,其实这是JVM的一个BUG,我们alijdk将这个问题进行了修复,能看到前后是有变化的,所以如果大家在排查Metaspace的问题时候,希望不要被这个信息骗到.

类加载器过多为什么会导致Full GC
类加载器创建过多,带来的一个问题是,在类加载器第一次加载类的时候,会在Metaspace里会给它分配内存块,为了分配高效,每个类加载器用来存放类信息的内存块都是独立的,所以哪怕你这个类加载器只加载一个类,也会为之分配一块空的内存给这个类加载器,其实是至少两个内存块,于是你有可能会发现Metaspace的内存使用率非常低,但是committed的内存已经达到了阈值,从而触发了Full GC,如果这种只加载很少类的类加载器非常多,那造成的后果就是很多碎片化的内存
1.    Orderd hint指示Oracle按照From字后的表顺序进行连接。如果没有及时收集统计信息,查询优化器没有得到足够信息,此时你可以自行选择适当的inner或者outer表进行连接。Oracle推荐使用Leading hint,它比ordred hint 有更多的用途;
2.    Leading hint 指示查询欧花旗使用指定的表作为连接的表首,即驱动表
3.    USE_NL hint 指示查询优化器使用nested loops方式连接指定表和其他行源,并且将强制指定表作为inner表;但如果此表同时作为outer表则忽略此hint,与以上两个hint搭配使用;
4.    Index hint 指示查询优化器对指定表使用索引扫描,依赖于是否明确指定index:
a)    指定一个可用,则使用此索引
b)    指定多个可用,则会使用成本最低的那个索引,或者扫描多个索引,然后合并结果。此种情况下,oracle推荐使用index_combine hint。
c)    没有指定和指定多个相同

Dubbo服务过程:
1.    Spring和Dubbo整合,BeanFactory解析出ServicBean(继承自config类)

Java1.8新特性 接口增强
    1.在接口中可以添加使用default关键字修饰的非抽象方法.
    2.接口中可以声明静态方法,并且可以实现

1.默认方法:
    允许接口中有使用default修饰的非抽象方法的实现-虚拟扩展方法,默认方法或防护方法;实现接口的时候,默认子类可以直接使用,类似于抽象类中的非抽象方法;(接口和抽象类的差别在减小);默认方法不能够重写Object的方法,但是可以重载Object的方法;默认方法不会强迫子类实现;
    现在在接口中可以添加默认方法,而不会影响编译;如果新增方法,是需要子类实现的,但是默认方法不会要求子类实现,且子类可以直接使用或者复写;
    在java1.8之后,Collection这一类的接口,新增了好多方法就是例子;
    默认方法的优缺点:
        优点:让接口的扩展性更强,对实现类的代码影响更小
        缺点:接口和类的界定越来越模糊.越来越类似于抽象类,多重继承之后如果不再重新界定方法的功能,可能会出现变异!
        例:
            接口A定义了默认打印方法:print a;
            接口B继承了接口A,重写了打印方法:print b;
            类C实现了接口B,在使用默认方法的时候回选择B的实现而不是A的
2.静态方法:
    接口中可以直接定义静态方法并使用;

3.java8中抽象类和接口
相同点:

1)都是抽象类型;

2)都可以有实现方法(以前接口不行);

3)都可以不需要实现类或者继承者去实现所有方法,(以前不行,现在接口中默认方法不需要实现者实现)

不同点:

1)抽象类不可以多重继承,接口可以(无论是多重类型继承还是多重行为继承);

2)抽象类和接口所反映出的设计理念不同。其实抽象类表示的是"is-a"关系,接口表示的是"like-a"关系;

3)接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值;抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。
    
friendly 型:如果一个类、类属变量及方法不以public,protected,private这三种修饰符来修饰,它就是friendly类型的,那么包内的任何类都可以访问它,而包外的任何类都不能访问它(包括包外继承了此类的子类),因此,这种类、类属变量及方法对包内的其他类是友好的,开放的,而对包外的其他类是关闭的。


定义布尔值的标识的时候使用基本类型而非引用类型,因为引用类型的默认值是Null,一个比较模棱两可的字段;

如果把success字段设置成isSuccess;JavaBeans(TM) Specification规定,普通参数要有get和set方法,布尔类型的会生成is加字段名,但是如果设置成前面的那种,很多IDE默认生成的时候回少一个is;
但是一般的序列化插件都会遵循JavaBean的命名规则,所以在解析的时候会将字段截取;

FOR UPDATE NOWAIT和 FOR UPDATE的区别
    for update:在执行加for update的sql之后,Oracle如果发现符合查询条件的数据被修改,则会等待修改提交之后才会继续执行该语句;
    for update nowait:Oracle如果发现符合查询条件的数据被修改,会返回ORA-00054错误,表示资源正在被占用;

下完整的综述一下Mina 的工作流程:
(1.) 当 IoService 实例创建的时候,同时一个关联在IoService上的IoProcessor 池、线程池也被创建;
(2.) 当 IoService 建立套接字(IoAcceptor 的bind()或者是IoConnector 的connect()方法被调用)时,IoService 从线程池中取出一个线程,监听套接字端口;
(3.) 当 IoService 监听到套接字上有连接请求时,建立IoSession 对象,从IoProcessor池中取出一个IoProcessor 实例执行这个会话通道上的过滤器、IoHandler;
(4.) 当这条IoSession 通道进入空闲状态或者关闭时,IoProcessor 被回收。上面说的是Mina 默认的线程工作方式,那么我们这里要讲的是如何配置IoProcessor 的多线程工作方式。
因为一个IoProcessor 负责执行一个会话上的所有过滤器、IoHandler,也就是对于IO 读写操作来说,是单线程工作方式(就是按照顺序逐个执行)。
假如你想让某个事件方法(譬如:sessionIdle()、sessionOpened()等)在单独的线程中运行(也就是非IoProcessor 所在的线程),那么这里就需要用到一个ExecutorFilter 的过滤器。
你可以看到IoProcessor 的构造方法中有一个参数是java.util.concurrent.Executor,也就是可以让IoProcessor 调用的过滤器、IoHandler 中的某些事件方法在线程池中分配的线程上独立运行,而不是运行在IoProcessor 所在的线程。

OrderedThreadPoolExecutor 这个线程池的worker的run方法没有对异常做处理,如果抛出异常,并不作处理,而是直接作为闲置线程放回线程池;

线程可以调用中断自己的方法interrupt()方法,但是中断也是有条件的,在线程调用interrupt()方法的时候虚拟机会在此线程上标记一个标志(这个中断标志只是一个布尔类型的变量),代表这个线程可能被中断,在后面的中断操作也是根据这个中断标志执行的。
可以说interrupt()方法是一种友好的方法,总是和虚拟机商量着来。如果一个线程处于了阻塞状态(如线程调用了sleep()、join()、wait()、以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则在线程在检查中断标示时如果发现中断标示为true,
则会在这些阻塞方法(sleep()、join()、wait()及可中断的通道上的 I/O 操作方法)调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。


DECODE(SIGN(10000 -ETFKnockQty)

内部类:非静态 静态 匿名
    打破了JAVA单继承特点,其实接口+内部类=多继承 

双亲委托机制的缺点:
    只会由下级的类加载器向上询问是否可以加载,但是没有逆向询问,
    如果说启动加载器中加载一个接口和一个提供该接口实现实例的工厂类,在APP加载器中加载该接口的实现类,那么就会出现无法通过工厂创建该实现的问题.
    只有下级加载器启动加载的类才可以在全文访问,所以 将 setContextClassLoader 方法和 getContextClassLoader 方法添加到了 java.lang.Thread 类中。这些方法允许该框架设置类装入器,使得在运行每个应用程序中的代码时可以将类装入器用于该应用程序。
    应该是仅限于当前的Thread    


在Java中,把核心类(rt.jar)中提供外部服务,可由应用层自行实现的接口,这种方式成为spi.
通过线程类Thread中的contextClassLoader实例来获取当前上下文的类加载器,所以就可以在启动类加载器中访问应用类加载器加载的类;也是通过先set在获取的;

解决死锁:
    线程中断;
    使用trylock进行限时尝试加锁;
    修改公平性;
lockInterruptibly();如果当前线程中断,则不会获取锁

创建字符串的两种方式:
    对象创建,地址存在栈中,地址指向堆中,堆引用常量池,如果存在
    直接创建,栈中创建变量,引用常量池中的字符串
    "a"+"b"只会产生一个字符串;
    str+str2 会产生3个字符串,相当于对象的创建;


    在启用安全管理器的时候,配置遵循以下基本原则:
        没有配置的权限表示没有。
        只能配置有什么权限,不能配置禁止做什么。
        同一种权限可多次配置,取并集。
        统一资源的多种权限可用逗号分割。

        非原子的64位操作
    Java存储模型要求获取和存储操作都是原子的,但对于double和long类型的变量,JVM允许见64位的读或写操作划分为两个32位操作。如果读和写发生在不同的线程,可能会出现得到一个值的高32位和另一个值的低32位。 
    如:开始读之前: a b
    Thread1更改为 :a1  b1      Thread2更改为:a2  b2
    Thread1与Thread2同时写入, Thread1写入高位a1在Thread2写入高位a2之前(a2覆盖a1),Thread2写入低位在Thread1写入低位之前(b1覆盖b2),最终结果得到: a2 b1

总结一下,LockSupport比Object的wait/notify有两大优势:

    ①LockSupport不需要在同步代码块里 。所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦。

    ②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序。
TimeUnit:用作时间转换,和延时
    延时写法:TimeUnit.时间单位常量(延时时间);

创建一个PK的时候,是自动创建一个与之对应的唯一索引的。
 如果不特别指定,那么这个索引的表空间和表格的空间是一样的,但是不建议放在一起。
CREATE TABLE BondPledgeNoCheckAccount (
    acctId                              VARCHAR2(12),                           --资金账号
    acctName                            VARCHAR2(40),                           --账号名称
    optId                               VARCHAR2(5),                            --操作柜员
    optTime                             NUMBER(14) DEFAULT to_number(to_char(sysdate,'yyyymmddhh24miss')), --操作时间
    CONSTRAINT PK_BondPledgeNoCheckAccount PRIMARY KEY (acctId)
    USING INDEX TABLESPACE STK_TS
)
TABLESPACE STK_TS
/

SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static要加锁,在JDK8之后可以使用Instant代替Date,LocalDateTime代替Calendr,DateTimeFormatter代替SimpleDateFormat

模板方法:AbstractQueuedSynchronizer 并没有实现任何同步接口

反射修改Final变量
    Field nameField = OneCity.class.getDeclaredField("name");
    
    Field modifiersField = Field.class.getDeclaredField("modifiers"); //①
    modifiersField.setAccessible(true);
    modifiersField.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL); //②
    
    nameField.setAccessible(true); //这个同样不能少,除非上面把 private 也拿掉了,可能还得 public
    nameField.set(null, "Shenzhen");
    System.out.println(OneCity.getName()); //输出 Shenzhen

       但是要防止final变量被关联 否则只是修改了Final 已编译的不能修改
 final的语义增强是在1.5之后,但是仅仅是在a的构造方法和a的final域的读写进行了内存屏障的插入,通过 final 属性安全上下文(final-field-safe context)保证final属性安全上下文中对final属性的读操作不会和相应的对于final属性的修改进行重排序,从而解决在构造之外final属性是否会重排序的问题;

 正常情况下如果使用Statement执行完一个查询,又去执行另一个查询时这时候第一个查询的结果集就会被关闭,也就是说,所有的Statement的查询对应的结果集是一个;
 不要使用ResultSet的next方法判空.. 使用ifBrforeFirst(),如果不为空则会指向第一个指针

 redis multi pipeline
平时使用redis的时候,经常有批量操作的需求;
比如批量读取一批数据,或者批量写入一批数据;
最习惯用的就是multiGet和multiSet,但是最近超时较多;
后来改用了redis的pipeline,超时较少,服务器压力也变小了;
这里来对比下multi操作与pipeline之间的区别
0.首先要明确,redis是单线程工作的;
    redis中负责主要工作的是主线程,主线程的工作包括但不限:接收客户端连接,处理连接读写事件,解析请求,处理命令,处理定时器事件,数据同步等相关工作。单进程单线程只能跑满一个CPU核,
在小包场景下,单个redis server的QPS在8~10万级别。如果QPS超过这个级别,单个redis server就无法满足需求。而常用的解决办法就是数据分片,采用多server的分布式架构予以解决。
然而数据分片,多redis server方式也存在若干问题:redis server过多,难以管理;分片之后一些在单redis server上使用的命令无法支持;分片无法解决热点读写问题;分片后数据倾斜
,数据重分布,数据扩缩容等也比较复杂。由于单进程单线程的局限,我们期望通过多线程的改造以期充分利用SMP多核架构的优势,从而达到提高单redis server吞吐的目的。对redis做多线程化,
最容易想到的方案是每个线程既做IO又做命令处理等工作,但由于redis处理的数据结构相对比较复杂,多线程需要锁来保证线程安全性,而锁粒度处理不好性能反而可能会出现下降。
我们的思路是通过增加IO线程,将连接中数据的读写,命令的解析和数据包的回复放到单独的IO线程来处理,而对命令的处理,定时器事件的执行等仍让单一的线程来处理,
以此达到提高单redis server吞吐的目的。
1.pipeline的原理是客户端缓冲所有命令,然后一次性发给服务端,目的是减少通信次数;
2.pipeline不具备原子性,可能部分成功,与其他客户端可以并发执行;
3.multi操作具备原子性;
4.实测pipeline效率比multi效率高10倍左右;
5.multi使用服务端缓冲,pipeline使用客户端缓冲;
6.multi是一条条发送的;


//实现锁的核心 继承抽象类AbstractQueuedSynchronizer 调用其中的加解锁方法 而实现共享锁的核心为 抽象类的state的值    

//位运算符是对单个相应的二进制来说的 
    & 按位与 都为1 则为1  否则为0
    | 按位或 一个为1  结果为1
    ^ 按位异或 两个二进制相同为1  不同为0
    ~ 将0 变为1 1 变为0
    << 左移n位 低位补0
    >> 右移n位 低位舍弃 无符号数 高位补0


    正数的符号位 为0 负数的为1
HashMap:
    在1.8之前,如果多个元素Hash冲突,将会放到同一个桶下形成链表结构,单链表查询慢,增删快,失去了map的hash优势,在1.8之后,会转化为红黑树,
    如果扩容前相邻的两个Entry在扩容后还是分配到相同的table位置上,就会出现死循环的BUG。在复杂的生产环境中,这种情况尽管不常见,但是可能会碰到。

多个对象指向一块内存;

atomic包中有一些weak前缀的方法,这个方法不会在改变值的时候对volatile前后添加内存屏障,不会创建happens-before原则,但是更高效一些;
The atomic classes also support method weakCompareAndSet, which has limited applicability. On some platforms, the weak version may be more efficient than compareAndSet in the normal case, but differs in that any given invocation of the weakCompareAndSet method may return false spuriously (that is, for no apparent reason). A false return means only that the operation may be retried if desired, relying on the guarantee that repeated invocation when the variable holds expectedValue and no other thread is also attempting to set the variable will eventually succeed. (Such spurious failures may for example be due to memory contention effects that are unrelated to whether the expected and current values are equal.) Additionally weakCompareAndSet does not provide ordering guarantees that are usually needed for synchronization control. However, the method may be useful for updating counters and statistics when such updates are unrelated to the other happens-before orderings of a program. When a thread sees an update to an atomic variable caused by a weakCompareAndSet, it does not necessarily see updates to any other variables that occurred before the weakCompareAndSet. This may be acceptable when, for example, updating performance statistics, but rarely otherwise.

一个原子类也支持weakCompareAndSet方法,该方法有适用性的限制。在一些平台上,在正常情况下weak版本比compareAndSet更高效,但是不同的是任何给定的weakCompareAndSet方法的调用都可能会返回一个虚假的失败( 无任何明显的原因 )。一个失败的返回意味着,操作将会重新执行如果需要的话,重复操作依赖的保证是当变量持有expectedValue的值并且没有其他的线程也尝试设置这个值将最终操作成功。( 一个虚假的失败可能是由于内存冲突的影响,而和预期值(expectedValue)和当前的值是否相等无关 )。此外weakCompareAndSet并不会提供排序的保证,即通常需要用于同步控制的排序保证。然而,这个方法可能在修改计数器或者统计,这种修改无关于其他happens-before的程序中非常有用。当一个线程看到一个通过weakCompareAndSet修改的原子变量时,它不被要求看到其他变量的修改,即便该变量的修改在weakCompareAndSet操作之前。

weakCompareAndSet atomically reads and conditionally writes a variable but does not create any happens-before orderings, so provides no guarantees with respect to previous or subsequent reads and writes of any variables other than the target of the weakCompareAndSet.

CyclicBarrier 的reset方法是在子线程执行完之前 如果想打破屏障才调用,正常情况下 ,如果所有子线程都到达屏障的时候,会自动重置;
CyclicBarrier 的原理不是 AQS 的共享模式,是 AQS Condition 和 ReentrantLock 的结合使用

CountDownLatch的原理:AQS 共享模式的典型使用,构造函数中的 1 是设置给 AQS 的 state 的。latch.await() 方法会阻塞,而 latch.countDown() 方法就是用来将 state-- 的,减到 0 以后,唤醒所有的阻塞在 await() 方法上的线程。

Phaser是比较灵活的实现, 使用父子节点的形式,通过对Long的cas实现栅栏,不需要固定栅栏的数量,只要注册和释放数量相等即可,且可以在中间栅栏不阻拦线程,而只在最后一个栅栏拦截;

import注解 加到类上,可以在不加注解的情况下将对应的类注入到指定类中 类似于静态导入的注解实现

@Conditional注解 按条件执行 是自定义配置覆盖自动配置的关健

静态方法上的锁 是类锁 不管new多少个对象 访问静态方法的时候都会被阻塞
非静态的锁是 对象锁 只有当前对象访问的时候才阻塞


 -verbose:class 会打印类装入过程的跟踪记录

javassist包只是对类的装载和修改,如果要执行类的文件;

dependencyManagement 只是在顶层模块中的版本管理 并不是依赖 只有在dependencies中的元素才是被依赖的;

IO 错误: Software caused connection abort: recv failed :在连接被回收的情况下 继续会用抛出的异常

FactoryBean 的作用其实是在实例化Bean的时候可以进行更多的操作,此类型的Bean在工厂getBean(name)的时候其实返回的是getObject的返回的对象的代理对象,在getobject方法中可以做很多处理
BeanFactory 就是IOC容器的核心接口 主要获取的就是当前name的实例,职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
ApplicationContext 是BeanFactory的派生类 功能进一步增强支持AOP 事件传递 消息发布 等

http的新的状态吗:
    428:要求先决条件
    429:太多请求
    431:请求头字段太大
    511:要求网络认证

在loadClass的时候 会有一个线程安全的Map存放每个类的锁对象; 如果map不存在 那么锁对象就是这个加载器 
    在加载的时候获取锁的两种情况 map的存在与否取决于 所加载的class是什么样的class

Void类是一个不可实例化的占位符类,如果方法返回值是Void类型,那么该方法只能返回null类型
        Class.getPrimitiveClass("void"); 是对Void的包装
        你使用泛型时函数并不需要返回结果或某个对象不需要值时候这是可以使用java.lang.Void类型表示

java支持分数....Fraction 

建造者模式 解决的问题其实还是组装多个属性的问题; 

单根继承:如果说一个类有了继承的类,那么实际上相当于他不在直接继承Object,但是也是Object类型,这能让Java对象都具有在堆上创建的可能性,比如hashCode,比如finalize,也能更好的和线程相联系,每个对象都具有锁的功能;其实还是Object的锁;垃圾回收器只要搜集一种对象即可;
对象只允许在堆上创建;

常量池是在元空间的,内存大小不占用JVM的内存,而是使用本地内存;

BigInteger对应的Int整形;
BigDecimal对应定点数 double float long 怕精度损失直接用;

Spring中真正可用的Bean工厂是DfaultListableBeanFactory,此工厂只存放bean名称,会把同类的归类存放,因为最终继承了DefaultSingletonBeanRegistry,这里才有Map容器,才能拿到最终的Object

在创建对象的时候,有可能是native方法, 其实是在调用C的malloc()分配内存;
当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。
当内存足够大的时候,gc有可能不会清理对象,即使强制GC;
  finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那么在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那么基本上它就真的被回收了。

  总结:finalize()并不是必须要执行的,它只能执行一次或者0次。如果在finalize中建立对象关联,则当前对象可以复活一次。

BeanPostProcessor:通过定义此类的bean,可以对全局所有的实例进行处理过滤等,相当于做了一个切面,其中postProcessBeforeInitialization方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization方法会在每个bean对象的初始化方法调用之后被回调;
BeanFactoryPostProcessor:通过定义此类的Bean,可以通过工厂类对Bean的定义BeanDefinition等进行修改,相当于强化!而上述的BeanPostProcessor仅限于Bean的修改;模拟双亲委托机制,进行定义的获取,这就是为什么定义多个工厂类的原因;
如果在BeanFactoryPostProcessor中提前让Bean实例化也是可以的,不过因为注解的依赖注入是依靠BeanPostProcessor进行的,所以如果B里需要注入A,啊哦做B的时候A会是空的;还有就是类似于服务暴露或者是接受请求的Bean,提前实例化会影响业务请求,
之前的Dubbo的服务就设置了懒加载,需要在容器完全起来之后在进行服务暴露;

Spring中的ConversionService它用来将前端传过来的参数和后端的 controller 方法上的参数进行绑定的时候用。

singleton 依赖 prototype,每次拿到的其实还是第一次初始化的Bean,因为相当于Bean的一个属性,只要singleton不销毁,就一直是一个;可以通过lookup-method来实现;
<bean id="singleton" class="singleton">
    <lookup-method name="方法名,空的也可以" bean="prototype"/>
</bean>

 replaced-method注入是spring动态改变bean里方法的实现。需要改变的方法,使用spring内原有其他类(需要继承接口org.springframework.beans.factory.support.MethodReplacer)的逻辑,替换这个方法。通过改变方法执行逻辑来动态改变方法。内部实现为使用cglib方法,重新生成子类,重写配置的方法和返回对象,达到动态改变的效果。
<bean id="实现" class="实现MethodReplacer的类"/>
<bean id="被替换方法的类" class="被替换方法的类">
    <replaced-method name="被替换的方法" replacer="实现">
    //可以不用写参数 写是为了防止重载
        <arg-type match="java.lang.String"></arg-type>
    </replaced-method>
</bean>
 Java 平台调试体系(Java Platform Debugger Architecture,JPDA)定义了一个完整独立的体系,它由三个相对独立的层次共同组成,而且规定了它们三者之间的交互方式,或者说定义了它们通信的接口。这三个层次由低到高分别是 Java 虚拟机工具接口(JVMTI),Java 调试线协议(JDWP)以及 Java 调试接口(JDI)。
    JVMTI(Java Virtual Machine Tool Interface)即指 Java 虚拟机工具接口,它是一套由虚拟机直接提供的 native 接口,它处于整个 JPDA 体系的最底层,所有调试功能本质上都需要通过 JVMTI 来提供。
        前身是 JVMDI 和 JVMPI;
    JDWP(Java Debug Wire Protocol)是一个为 Java 调试而设计的一个通讯交互协议,它定义了调试器和被调试程序之间传递的信息的格式。在 JPDA 体系中,作为前端(front-end)的调试者(debugger)进程和后端(back-end)的被调试程序(debuggee)进程之间的交互数据的格式就是由 JDWP 来描述的,它详细完整地定义了请求命令、回应数据和错误代码,保证了前端和后端的 JVMTI 和 JDI 的通信通畅。
    JDI(Java Debug Interface)是三个模块中最高层的接口,在多数的 JDK 中,它是由 Java 语言实现的。 JDI 由针对前端定义的接口组成,通过它,调试工具开发人员就能通过前端虚拟机上的调试器来远程操控后端虚拟机上被调试程序的运行,JDI 不仅能帮助开发人员格式化 JDWP 数据,而且还能为 JDWP 数据传输提供队列、缓存等优化服务。
JPDA 定义了一套如何开发调试工具的接口和规范。
    JPDA 由三个独立的模块 JVMTI、JDWP、JDI 组成。
    调试者通过 JDI 发送接受调试命令。
    JDWP 定义调试者和被调试者交流数据的格式。
    JVMTI 可以控制当前虚拟机运行状态。


Java Agent:Java探针技术,用于修改或增强Java的字节码文件,许多热部署的插件和上线诊断工具就在此基础实现,此类程序和普通Jar包不同,必须依附于程序才能进行启动;
    程序运行前加载

    通过JVM参数 -javaagent:**.jar启动,程序启动的时候,会优先加载Java Agent,并执行其 premain方法,这个时候,其实大部分的类都还没有被加载,这个时候可以实现对新加载的类进行字节码修改,但是如果 premain方法执行失败或抛出异常,那么JVM会被中止,这是很致命的问题。

    程序运行后加载

    程序启动之后,通过某种特定的手段加载Java Agent,这个特定的手段就是 VirtualMachine的 attach api,这个api其实是JVM进程之间的的沟通桥梁,底层通过socket进行通信,JVM A可以发送一些指令给JVM B,B收到指令之后,可以执行对应的逻辑,比如在命令行中经常使用的jstack、jcmd、jps等,很多都是基于这种机制实现的。

感觉是JDI的调用者,然后JDI格局JDWP协议去驱动JVMTI;    

Javassist (JAVA programming ASSISTant) :第三方的反射工具,获取CtClass类型,可以修改.class文件,相比Class的反射来说更加的暴力,已经写过例子了com.example.thread.Jassist1;
ASM、Javassist和byteBuddy三种框架,都可以实现

ListIterator和Iterator的区别

1.使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。

2.ListIterator有add方法,可以向List中添加对象,而Iterator不能。

3.ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。

4.ListIterator可以定位当前索引的位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。

5.都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。

桥接方法:在JDK1.5之后引入了泛型,子类在重写父类的方法之后,参数是父类的子类的时候,如果没有桥接方法,实质上是重载,桥接方法会转换相应的类型来调用实际的方法;

重写Object的方法可以不经过主动调用,在需要的时候触发,比如toString();

使用基本数据类型在内存中的位的表示,可以利用位的分段表示不同的状态,因为使用位移来计算,更高效一些;比如在ThreadPoolExcutor使用Integer的32位表示线程池的状态;

NIO:
    三大组件:
            Buffer:数据
            Channel:缓冲区
            Selector:管理缓冲区,通过一个SelectionKey来和Channel关联,属于一对多的关系
    通过channel.register(selector, SelectionKey.OP_READ,attr)或者selector.register(channel, SelectionKey.OP_READ,attr)来关联,并根据第二个参数决定selector可以监听什么样的事件;

AIO多路复用IO:Asynchronous前缀的NIO的类,返回future,线程执行的结果对象

可以通过Tomcat类的main方法启动Tomcat,应用于嵌入式的服务器,配置默认;

一个标准的缓存的特点:
    过期时间(存活期:创建到到期,空闲期:没有被访问移除时间),容量规划、清除策略(先进先出,最久未使用,最近最少使用),命中率统计(防止穿透和雪崩);

现在一般都是使用Map做本地缓存,弱引用的weakhashMap虽然可以自动回收内存,但是回收的是一整个map而不是某个键,但是避免不了下次加载,Guava Cache是也是基于Map实现的,但是包装了一层,做了一些标准缓存的框架
除此之外还有Ehcache,Memcached等,这些都有自己的策略实现,比较容易配置和与现有框架兼容;

尽量用关联查询代替子查询,尽量减少和数据库的交互次数;

反射执行set方法现成例子 RocketMQ的Broker配置类抄的;
Method[] methods = object.getClass().getMethods();
        for (Method method : methods) {
            String mn = method.getName();
            if (mn.startsWith("set")) {
                try {
                    String tmp = mn.substring(4);
                    String first = mn.substring(3, 4);

                    String key = first.toLowerCase() + tmp;
                    String property = p.getProperty(key);
                    if (property != null) {
                        Class<?>[] pt = method.getParameterTypes();
                        if (pt != null && pt.length > 0) {
                            String cn = pt[0].getSimpleName();
                            Object arg = null;
                            if (cn.equals("int") || cn.equals("Integer")) {
                                arg = Integer.parseInt(property);
                            } else if (cn.equals("long") || cn.equals("Long")) {
                                arg = Long.parseLong(property);
                            } else if (cn.equals("double") || cn.equals("Double")) {
                                arg = Double.parseDouble(property);
                            } else if (cn.equals("boolean") || cn.equals("Boolean")) {
                                arg = Boolean.parseBoolean(property);
                            } else if (cn.equals("float") || cn.equals("Float")) {
                                arg = Float.parseFloat(property);
                            } else if (cn.equals("String")) {
                                arg = property;
                            } else {
                                continue;
                            }
                            method.invoke(object, arg);
                        }
                    }
                } catch (Throwable ignored) {
                }
            }
        }

RocketMQ:
    admin:控制台界面,可以进行topic的创建,消息的创建,删除以及发送;
    producer:消息生产者,有两种生产者,一种是生产事务消息(需要用到group属性来唯一确认),一种是生产普通消息
    consumer:消息消费者,有两种消费者,
        一种是push(启动生产者的推送线程),并不知道消费者的消费情况,会出现堆积,超过消费者缓冲区最大限制会出问题;
            订阅消息,注册监听器,由生产者和broker协作推送消息,偏移量在broker处维护,消费者的工作量少一些
        一种是pull(主动拉取消息,可以自定义位置消费),拉取的频率需要设计;
            根据消息去找到队列,遍历返回给消费者,偏移量在消费者处维护,代码多一些,但是比较好控制,发挥的空间大
            包含同步和异步的拉取,如果没有消息又分为等待和不等待;
    broker:核心,存储生产者消费者的实例信息,存储topic信息,会注册到nameServer,与生产者和消费者进行交互,维护队列和偏移量;存储一些收发消息的Filter信息;
        只支持Master写,salver读,而且只有master压力过高才会将读压力转换给salver;无法主从切换;slaver启动会上报消费偏移,但是Master不会去slaver同步;
    nameServer:轻量级的注册中心,可以集群部署,互相之间透明存在,每个节点都保持与存活的broker的通信,所以包含的信息是一样的,不需要进行集群的选举,负载的实现比较简单,也是消费者的注册中心;
    生产者和消费者主动通过nameServer了解Broker,并和Broker进行连接,更新一些信息;

    SpringBoot和普通的SpringWeb工程的启动顺序是反的,

Java中的方法调用 可以采用函数式编程
  Person p=new Person();
  BlockedCallback setAge = p::setAge;
  返回的是一个函数式接口;
  在SpringBoot启动Tomcat的时候,将一个函数式的接口当做参数传递,然后统一调用值得学习;
 

   
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值