文章目录
  1. 1. zk分布式锁:
  2. 2. 谈谈分布式
  3. 3. redis持久化实现原理
  4. 4. 生产者消费者
  5. 5. JVM的内存模型
  6. 6. JVM体系结构
  7. 7. 常用GC算法
  8. 8. 什么情况下出现Full GC,什么情况下出现Young GC
  9. 9. http1.0,http1.1,http2.0区别
  10. 10. Get/Post对缓存的影响
  11. 11. HTTP请求头和响应头
  12. 12. 通过三种方式编写单例模式
  13. 13. 编程技术书籍
  14. 14. NIO核心组件
  15. 15. 判断对象可收回
  16. 16. 中文编码问题
  17. 17. Spring的优缺点
  18. 18. 单例模式的优缺点
  19. 19. 唯一索引的作用
  20. 20. 共享锁和排他锁
    1. 20.1. 共享锁
    2. 20.2. 排他锁
  21. 21. 数据库隔离级别
  22. 22. 数据库隔离级别出现的问题
  23. 23. 分布式、集群、负载均衡的理解
    1. 23.1. 负载均衡算法
  24. 24. 悲观锁和乐观锁
    1. 24.1. 适用场景:

zk分布式锁:

详情参考:http://www.jiacheo.org/blog/620

1.在单机模式下,没有引入zookeeper时,我们可以通过创建一个临时文件加锁,然后在事务处理完毕后,将临时文件删除就代表解锁。这种加锁和解锁模式可以移植到zookeeper上,通过创建一个路径,来证明该锁已经存在,然后删除路径来释放该锁。而同时zookeeper又能支持对节点的监控,这样一来,我们在多机的情况下就能同时且实时知道锁是存在还是已经解锁了。

2.具体流程:

  • 在zookeeper存储结构中,假设有一个lock目录。
  • 在该目录下创建自增长的临时节点,这个节点上的数字用于表示获取锁的先后顺序。
  • 假设有三台服务器请求zookeeper创建了,00001、00002、00003 三个节点。然后判断lock目录下的最小节点和自己创建的是否一致,如果一致可以获取锁,如果不一致执行等待操作。
  • 当获取锁的节点执行完后,删除节点,并通知其他节点,在进行比较。

3、分布式锁出现死锁,发生死锁的情况主要出现在网络的情况下:

  • 出现方式
    • 假设zk服务收到了请求,子节点创建成功。但是返回给客户端的时候网络发生异常。这时候我们重试,创建的话,就会进入死锁。这里的死锁和平常的死锁不一样,不是回路循环等待,而是相当于这个锁死掉了。因为这个流程其实已经创建了一个节点,但是这个节点没有与客户端关联,称为幽灵节点。
  • 解决方法
    • 创建节点的时候,将客户端作为节点的一部分,也就是每个节点都有唯一标识的信息。
    • 重试的时候,对每个节点进行判断,对于当前进程创建的节点不重复创建。

谈谈分布式

  • 互联网应用满足要求:高吞吐、高并发、低延迟、负载均衡
    • 高吞吐,意味着你的系统,可以同时承载大量的用户使用。
    • 高并发,做尽量多的处理,同时处理多个任务。
    • 低延迟,大量用户访问时,也能很快返回计算结果。
    • 负载均衡,同时发生的请求,有效的让多个不同服务器承载。
  • 分布式系统提高承载量的基本手段
    • 分层模型
    • 并发模型
    • 缓存技术
    • 存储技术(NoSql)

redis持久化实现原理

redis持久化

生产者消费者

JVM的内存模型

  • 内存空间(Runtime Data Area)中可以按照是否线程共享分成两块,线程共享的是方法区(Method Area)和堆(Heap),线程独享的是Java栈(Java Stack),本地方法栈(Native Method Stack)和PC寄存器(Program Counter Register)。
  • 在 JDK 1.7 及以往的 JDK 版本中,Java 类信息、常量池、静态变量都存储在 Perm(永久代)里。类的元数据和静态变量在类加载的时候分配到 Perm,当类被卸载的时候垃圾收集器从 Perm 处理掉类的元数据和静态变量。当然常量池的东西也会在 Perm 垃圾收集的时候进行处理。

JVM体系结构

  • 类加载器
    在JVM启动时或者在类运行时将需要的class加载到JVM中。

  • 执行引擎
    执行引擎的任务是负责执行class文件中包含的字节码指令,相当于实际机器上的CPU

  • 内存区
    将内存划分成若干个区域以模拟实际机器上的存储,记录和调度功能模块

  • 本地方法调用
    调用C或C++实现的本地方法的代码返回结果

常用GC算法

  • 引用计数法
  • 复制法
    原理:从根集合开始,通过追踪从From中找到存活对象,拷贝到To中;From和To交换身份,下次内存分配从To开始
    优缺点:没有标记和清除的过程,效率高;没有内存碎片,可以快速实现内存分配;但是需要双倍空间
    GC的复制法.jpg
  • 标记清除法
    原理:分为两个阶段,标记和清除;标记,从根集合开始扫描,对存活的对象进行标记。清除,扫描整个内存空间,回收未被标记的对象。
    优缺点:不需要额外空间,但是,两次扫描,耗时严重;会产生碎片
    GC标记清除法标记阶段
    GC标记清除法清除阶段
  • 标记压缩法
    原理:分为两个阶段,标记和压缩;标记,从根集合开始扫描,对存活对象进行标记。压缩,再次扫描,并往一端滑动存活对象。
    优缺点:没有内存碎片,但是需要移动对象的成本
    GC标记压缩法标记阶段
    GC标记压缩法压缩阶段
  • 标记清除压缩法
    原理:将标记清除和标记压缩相结合,过程和标记清除一样,但是进行多次GC后才压缩。
    优缺点:减少了对象的移动成本

什么情况下出现Full GC,什么情况下出现Young GC

  • 对象优先在新生代Eden区分配,如果Eden区没有足够的空间时,就会触发一次young gc
  • full gc触发的条件有多个,且full gc的时候会出现stop world。
    • 在执行yong gc之前,JVM会进行空间担保-如果老年代的连续空间小于新生代对象的总大小(或历次晋升的平均大小),则触发一次full gc。
    • 显示的调用System.gc()
    • 大对象直接进入老年代,从年轻代晋升上来的老对象,尝试在老年代分配内存时,但是老年代内存不足时
    • 永生区空间不足时
    • jmap和jcmd dump文件时候会触发FGC

http1.0,http1.1,http2.0区别

Get/Post对缓存的影响

  • 不支持Post Method。Get可以被浏览器缓存。

HTTP请求头和响应头

  • HTTP请求头
请求头 说明
Accept-Charset 用于指定客户端接受的字符集
Accept-Encoding 用户指定可接受的内容编码
Accept-Language 用于指定一种自然语言,如果Accept-Language:zh-cn
Host 用于指定被请求资源的Internet主机和端口号
User-Agent 客户端将它的操作系统、浏览器和其他属性告诉服务器
Connection 当前连接是否保持
  • HTTP响应头
请求头 说明
Server 使用的服务器名称
Content-Type 用来指定响应的实体正文的类型
Content-Encoding 与请求报头Accept-Encoding对应,告诉浏览器服务器端采用的是什么压缩编码
Content-Language 描述资源所用的自然语言,与Accept-Language对应
Content-Length 指明实体正文的长度,用以字节方式存储的十进制数字来表示
Keep-Alive 保持连接的时间

通过三种方式编写单例模式

  • 静态内部类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Singleton{
    private static class SingletonHolder{
    private static final Singleton INSTANCE = new Singletion();
    }
    private Singletion(){}
    public static final Singletion getInstance(){
    return SingletionHolder.INSTANCE;
    }
    }
  • 枚举

    1
    2
    3
    public enum Singleton{
    INSTANCE;
    }
  • 饿汉式

    1
    2
    3
    4
    5
    6
    7
    public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
    return instance;
    }
    }

    编程技术书籍

    https://github.com/jobbole/awesome-programming-books

NIO核心组件

  • Channel,可以理解为资源的一个流,通过这个流资源可以从Channel读取Data到一个Buffer中或者从一个Buffer中写入Data到Channel
  • Buffer,java NIO Buffers 和Channels配合使用
  • Selectors,Selector组件可以运行判断多个Channel,动态决定使用哪个Channel来执行Read 或者 Write操作。通过这个组件可以上线一个Thread 管理多个Channels 或者 多个网络连接Channel。

判断对象可收回

  • 可达性分析
    算法的基本思想是通过一系列被称为”GC Root” 的对象作为起点,从这些起点向下搜索,搜索所有的路径,这些路径称为引用链,当一个对象到GC Root没有任何引用链时,则证明此对象是不可用的可回收;GC Root对象包括

    • System class

      系统加载类和启动加载类。例如 rt.jar 包中的都是,比如 java.util.*

    • 虚拟机栈中引用的对象

    • 方法区中的类静态属性引用的对象

    • 方法区中常量引用的对象

    • 本地方法中引用的对象

  • 引用类型判断
    JDK有四种引用,分别是强引用、软引用、弱引用、虚引用

    • 强引用

      强引用就是我们平常用的类似于“Object obj = new Object()”的引用,只要obj的生命周期没结束,或者没有显示地把obj指向为null,那么JVM就永远不会回收这种对象

    • 软引用

      软引用相对强引用来说就要脆弱一点,JVM正常运行时,软引用和强引用没什么区别,但是当内存不够用时,濒临逸出的情况下,JVM的垃圾收集器就会把软引用的对象回收。具体演示如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      public class SoftReferenceTest {
      public static void main(String[] args) {
      SoftReferenceTest softReferenceTest = new SoftReferenceTest();
      SoftReference<SoftReferenceTest> softReference = new SoftReference<>(softReferenceTest);
      softReferenceTest = null;
      System.out.println("before softReference:" + softReference.get());
      int i = 0;
      ArrayList<String> list = new ArrayList<>();
      while (i++<100000000){
      list.add(String.valueOf(i));
      }
      System.out.println("end");
      }
      @Override
      protected void finalize() throws Throwable {
      super.finalize();
      System.out.println("SoftReferenceTest finalize");
      }
      }

      当调小值的时候不会执行finalize()方法

    • 弱引用

      弱引用比软引用更加脆弱,弱引用的对象将会在下一次的gc被回收,不管JVM内存被占用多还是少。

    • 虚引用

      虚引用是最脆弱的引用

中文编码问题

  • 中文变成了看不懂的字符
    字符串在解码时所用的字符集与编码字符集不一致导致。
    1
    2
    3
    4
    5
    6
    String encode = URLEncoder.encode("淘!我喜欢","GBk");
    System.out.println(encode);
    String errorDecode = URLDecoder.decode(encode, "ISO-8859-1");
    System.out.println(errorDecode);
    String decode = URLDecoder.decode(encode, "GBk");
    System.out.println(decode);

输出如下

1
2
3
encode:%CC%D4%A3%A1%CE%D2%CF%B2%BB%B6
errorDecode:ÌÔ£¡ÎÒϲ»¶
decode:淘!我喜欢

  • 一个汉字变成一个问号
    将中文和中文符号经过不支持中文的ISO-8859-1编码后,所有字符变成 ?,这是因为使用ISO-8859-1 进行解码时,遇到不在码值范围内的字符会统一用3f表示,这也是常说的“黑洞”,所以ISO-8859-1 不认识的字符都会变成’?’。
    1
    2
    3
    4
    String encode = URLEncoder.encode("淘!我喜欢", "ISO-8859-1");
    System.out.println("encode:"+encode);
    String decode = URLDecoder.decode(encode, "ISO-8859-1");
    System.out.println("decode:"+decode);

输出为:

1
2
encode:%3F%3F%3F%3F%3F
decode:?????

  • 一个汉字变成两个问号
    这种情况比较复杂,中文经过多次编码,但是其中一次编码或者解码不对仍然会出现中文字符变成’?’的情况

Spring的优缺点

优点

  • 提供了一种管理对象的方法,可以把中间层对象有效地组织起来。一个完美的框架“黏合剂”。
  • 采用了分层结构,可以增量引入到项目中。
  • 有利于面向接口编程习惯的养成。
  • 目的之一是为了写出易于测试的代码。
  • 非侵入性,应用程序对Spring API的依赖可以减至最小限度。
  • 一致的数据访问界面。
  • 一个轻量级的架构解决方案。

 缺点:

  • 中断了应用程序的逻辑,使代码变得不完整,不直观。此时单从Source无法完全把握应用的所有行为。
  • 将原本应该代码化的逻辑配置化,增加了出错的机会以及额外的负担。
  • 调试阶段不直观,后期的bug对应阶段,不容易判断问题所在。

单例模式的优缺点

优点:

  • 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
  • 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
  • 提供了对唯一实例的受控访问。
  • 由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
  • 允许可变数目的实例。
  • 避免对共享资源的多重占用。

缺点:

  • 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
  • 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

唯一索引的作用

共享锁和排他锁

共享锁

共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务不能对数据进行修改(获取数据上的排他锁)。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获取共享锁的事务只能读数据,不能修改数据。

排他锁

排他锁又称写锁,如果事务T对数据A加上排他锁后,其他事务不能再对A加任何类型的锁。获取排他锁的事务既能读数据,又能修改数据。

数据库隔离级别

  • 读未提交的,另一个事务修改了数据,但尚未提交,而本事务中的select会读到这些未被提交的数据(也就是脏读)。脏读就是指另一个事务修改了数据,但尚未提交,而本事务中的select会读到这些未被提交的数据
  • 读已提交的,本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后执行相同的 select会读到不同的结果(不可重复读)。不可重复读,指同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果不一致
  • 可重复读,在同一事务中,SELECT的结果是事务开始时间点的状态,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象。可重复读保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会发现了这些新数据,貌似之前读到的数据是幻觉,这就是幻读。
  • 串行化,所有事务只能一个个执行,不能并发
  • 数据库隔离级别出现的问题

    • 脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有提交的字段,之后,若T2回滚,T1读取到的内容就是临时无效的内容。
    • 不可重复读:对于事务T1,T2,T1需要读取一个字段两次,在第一次和第二次读取之间,T2更新了该字段,导致T1第二次读取到的内容值不同。出现这样的原因是,T1事务读取完数据后释放了共享锁,导致T2可以获取排他锁对数据进行修改,所以第二次看到数据不一致。
    • 幻读: 事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。 幻读与不可重复读之间的区别是幻读强调的是新增或删除,而不可重复读强调的是修改。这是在可重复读的事务级别下出现的现象,在该事务级别下,T1读完数据后,不到事务提交是不会释放共享锁的,也就是其他事务不能获取到排他锁对已读到的数据进行修改,实现了可重复读;但是其他事务可以执行insert语句,这样就导致出现幻读。

分布式、集群、负载均衡的理解

集群:同一个业务,部署在多个服务器上。
分布式:一个业务分拆成多个子业务,或者本身就是不同的业务,部署在不同的服务器上。
负载均衡:通过负载均衡算法,将用户的请求转发给后台内网服务器。达到访问的负载均衡

负载均衡算法

  • 轮询(RoundRobin)将请求顺序循环地发到每个服务器。当其中某个服务器发生故障,AX就把其从顺序循环队列中拿出,不参加下一次的轮询,直到其恢复正常。
  • 比率(Ratio):给每个服务器分配一个加权值为比例,根椐这个比例,把用户的请求分配到每个服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
  • 优先权(Priority):给所有服务器分组,给每个组定义优先权,将用户的请求分配给优先级最高的服务器组(在同一组内,采用预先设定的轮询或比率算法,分配用户的请求);当最高优先级中所有服务器或者指定数量的服务器出现故障,AX将把请求送给次优先级的服务器组。这种方式,实际为用户提供一种热备份的方式。
  • 最少连接数(LeastConnection):AX会记录当前每台服务器或者服务端口上的连接数,新的连接将传递给连接数最少的服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
  • 最快响应时间(Fast Reponse time):新的连接传递给那些响应最快的服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
  • 哈希算法( hash): 将客户端的源地址,端口进行哈希运算,根据运算的结果转发给一台服务器进行处理,当其中某个服务器发生故障,就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
  • 基于数据包的内容分发:例如判断HTTP的URL,如果URL中带有.jpg的扩展名,就把数据包转发到指定的服务器。

悲观锁和乐观锁

  • 悲观锁(Pessimistic Lock):
    顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会Lock直到它拿到锁。

  • 乐观锁(Optimistic Lock):
    顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。

适用场景:

  • 悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。

  • 乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

    总结:两种所各有优缺点,读取频繁使用乐观锁,写入频繁使用悲观锁。

文章目录
  1. 1. zk分布式锁:
  2. 2. 谈谈分布式
  3. 3. redis持久化实现原理
  4. 4. 生产者消费者
  5. 5. JVM的内存模型
  6. 6. JVM体系结构
  7. 7. 常用GC算法
  8. 8. 什么情况下出现Full GC,什么情况下出现Young GC
  9. 9. http1.0,http1.1,http2.0区别
  10. 10. Get/Post对缓存的影响
  11. 11. HTTP请求头和响应头
  12. 12. 通过三种方式编写单例模式
  13. 13. 编程技术书籍
  14. 14. NIO核心组件
  15. 15. 判断对象可收回
  16. 16. 中文编码问题
  17. 17. Spring的优缺点
  18. 18. 单例模式的优缺点
  19. 19. 唯一索引的作用
  20. 20. 共享锁和排他锁
    1. 20.1. 共享锁
    2. 20.2. 排他锁
  21. 21. 数据库隔离级别
  22. 22. 数据库隔离级别出现的问题
  23. 23. 分布式、集群、负载均衡的理解
    1. 23.1. 负载均衡算法
  24. 24. 悲观锁和乐观锁
    1. 24.1. 适用场景: