自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(71)
  • 问答 (3)
  • 收藏
  • 关注

原创 创建型模式之工厂模式

  工厂模式是一种常见的设计模式,通过定义一个创建对象的接口,让子类决定将哪个工厂类实例化。

2021-12-21 16:51:35 153

原创 依赖倒置原则

&emsp 依赖倒置指的是高层模块不应该依赖于底层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。未优化案例  案例中以抽奖场景为背景,设计了两种不同的抽奖方式(抽奖逻辑对不对先不管,主要是为了演示)。public class BetUser { private String userName; // 用户姓名 private int userWeight; // 用户权重 public BetUser() { }

2021-12-10 22:52:49 264

原创 接口隔离原则

  接口隔离原则的定义是客户端不应该被迫依赖于它不使用的接口,一个类对另一个类的依赖应该建立在最小的接口上。接口隔离原则要求程序员尽量将庞大的接口拆分成更小而具体的接口。当然,接口不是越小越好,一个接口应该值服务于一个子模块或逻辑,更具业务需求设计接口,减少对外交互,提供调用者需要的方法,屏蔽不需要的方法。未优化例子  以王者荣耀的英雄和技能为例,将英雄技能抽象为接口方法,两个英雄实现该接口。public interface ISkill { //灼日之矢 void doArch

2021-12-10 22:22:51 437

原创 迪米特法则原则

  迪米特法则又称为最少知道原则,一个对象类对于其他类来说,知道的越少越好。两个类之间不要有过多的耦合,保持最少的关联,有内在联系的类要高内聚,没有直接关系的类应该低耦合。未优化案例例子里有老师、学生、校长三者,老师负责了解每一个学生的成绩而校长值关系班级总成绩和平均分,即班级的总体情况。public class Student { private String name; // 学生姓名 private int rank; // 考试排名(总排名) p

2021-12-10 11:19:19 294

原创 里氏替换原则

  如果S是T的子类,那么所以T类型的对象都可以被S类型的对象替换。也就是说子类可以扩展父类的功能但不能改变原有的功能,尽量不重新父类的方法。里氏替换原则包含四点:1、子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。2、子类可以增加自己的方法。3、子类重载父类的方法时,方法的入参要比父类更宽松。4、子类方法重写、重载父类方法或实现抽象方法时,方法的输出或返回应该比父类更严格或相等。里氏替换原则的作用1、是实现开闭原则的重要方式。2、解决了重写父类方法导致复用性变差的问题。3、类的扩展不会给原

2021-12-10 10:44:32 255

原创 六大设计原则之开闭原则

  开闭原则要求对象、类、模块、函数对扩展开发,对修改封闭。所以要用抽象来定义结构,用实现扩展细节,可以理解为面向抽象编程,定义接口并实现方法,在扩展时通过继承的方式扩展。案例public interface ICalculationArea { //计算长方形面积 double rectangle (double x, double y); //计算三角形面积 double triangle(double x, double y, double z);

2021-12-09 23:14:42 240

原创 单一职责原则

  单一职责又称单一功能原则,规定一个类应该只有一个发生变化的原因。职责指发生变化的原因,如果一个类有多个原因引起变化,那么后续迭代维护和拓展可能会带来很多麻烦。以视频网站用户分类为例简单地假设某个视频网站的用户分为访客用户、普通会员、VIP会员三类。访客:只能观看480P高清,有广告;普通会员:可以观看720P超清,有广告;VIP会员:可以观看1080P蓝光,无广告。  简单地直接实现的方式:public class VideoUserService { public void s

2021-12-09 21:55:25 109

原创 CycilcBarrier

CycilcBarrier的应用场景为多个线程进行不同阶段的任务,当所有线程到达await()后指定的任务才会被执行。CycilcBarrier的结构  CycilcBarrier内部包含几个属性public class CyclicBarrier { /** The lock for guarding barrier entry */ private final ReentrantLock lock = new ReentrantLock(); /** Condit.

2021-12-08 13:24:07 226

原创 CountDownLatch

类结构 public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }  构造函数用传入的count初始化状态数,CountDownLatch内只有一个Sync类型的属性。Sync结构private static final class Syn.

2021-12-08 10:54:21 174

原创 Semaphore

Semaphore的结构  构造函数: public Semaphore(int permits) { sync = new NonfairSync(permits); } public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); }  Semaphore内部有一个Sync类.

2021-12-07 22:59:43 307

原创 ConcurrentHashMap

ConcurrentHashMap源码剖析

2021-12-06 14:25:58 389

原创 StampedLock

为什么要引入StampedLcok  重入锁、读写锁、StampedLock的并发度对比。:  StampedLock读和写不互斥,并发度更高。StampedLock用乐观读提高并发度。  基本用法:public class Point { private double x, y; private final StampedLock stampedLock = new StampedLock(); void move(double deltaX, double del.

2021-08-01 17:54:42 115

原创 happen-before与volatile、final

重排序与内存可见性问题  重排序有三种:(1)编译器重排序:编译器可以对没有先后依赖关系的语句重新排序。(2)CPU指令重排序:对没有依赖关系的指令重新排序。(3)CPU内存重排序:指令执行顺序和CPU缓存写入主内存的顺序不一致。  内存可见性问题主要是第三种重排序造成的。以下面例子为例:线程1:X = 1 ; a = Y线程2:Y = 1 ; b = X理论上来看,最后可能出现三种情况:1、a = 0,b = 1。2、a = 1,b = 1。3、a = 1,b = 0。但是,a和b都为0也.

2021-06-27 20:47:52 242

转载 注解的原理

怎么定义一个注解  注解可以在类名前加上@interface将一个类定义为注解。元注解  元注解有五个:1、@Retention:应用在一个注解上时,用来说明这个注解的存活时间,它内部定义了三个生命周期。public enum RetentionPolicy { // 在编译前注解将被丢弃 SOURCE, // 注解被保留到编译时时 CLASS, // 注解保留到运行时 RUNTIME}2、@Documented:将注解中的元素包含到Javadoc

2021-06-24 23:43:05 289

原创 JVM对synchronized的优化和锁状态

锁偏向  如果一个线程获得了锁,那么锁就进入了偏向模式。当这个线程再次请求锁时,不用再进行同步操作,减少了锁申请的操作。在没有锁竞争或者竞争少的情况下,有较好的性能,而在锁竞争大的情况下,可能每次获取锁的线程都不同,所以效果不好。轻量级锁  进入偏向锁失败后,JVM不会立即挂起线程,会进入轻量级锁优化。将对象头部作为指针,指向持有锁的线程堆栈,判断一个对象是否持有锁 。如果获取轻量级锁失败,当前的锁请求会膨胀为重量级锁。自旋锁  线程获取锁失败后,系统假设在不久后线程能获取到锁,JVM.

2021-06-24 18:31:57 112

原创 线程池的4种拒绝策略

  线程池在提交任务的时候如果线程池的状态不是运行状态或者线程数达到maxPoolSize,会执行拒绝策略reject(Runnable command)。// 策略1:让调用者执行线程public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable

2021-06-23 23:01:06 226

原创 线程池的执行过程

private final class Worker extends AbstractQueuedSynchronizer implements Runnable{ Worker(Runnable firstTask) { setState(-1); // 初始状态设为-1 this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this)

2021-06-23 21:30:33 172

原创 线程池的任务提交

public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { // 如果线程数小于corePoolSize则创建新线程 if (addWorker(

2021-06-23 20:18:45 424

原创 线程池的结构与关闭原理

线程池实现原理  线程池的原理是:调用方的线程向线程池的队列中提交任务,线程池的线程从队列中取任务进行处理。实现一个线程池需要考虑的问题  1、队列设置多长?如果是无界队列可能会因为队列过长导致内存耗尽,如果是有界队列,队列满了该怎么处理。  2、线程池中的线程数是固定的还是动态变化的?  3、提交任务时,是放入队列还是创建新线程。  4、没有任务时线程是进入睡眠还是阻塞,如果阻塞该怎么唤醒?问题4通常有3种做法:(1)不使用阻塞队列,使用线程安全的队列,没有阻塞–唤醒机制。当队列为空时

2021-06-23 19:42:42 261 1

原创 Condition与Lock

Condition与Lock的方法  Condition与Lock是两个接口,以上是它们内部定义的方法。Lock中有一个newCondition方法,所以Condition都是从Lock中创建出来的。Condition实现原理  以读写锁为例来看原理:public class ReentrantLock implements Lock, java.io.Serializable { ... public Condition newCondition() { return sy

2021-06-22 22:51:13 914 1

原创 ReentrantLock基于AQS的实现

阻塞队列的结构  最开始时,head = tail = NULL,当加入队列时,会先创建一个空节点,然后再往里面插入节点,所以当head == tail时,说明队列为空。lock()的实现  acquire()的实现:tryAcquire()用来再次尝试获取锁,acquierQueued()用来将线程放进阻塞队列。![在这里插入图片描述](https://img-blog.csdnimg.cn/20210621234358610.png?x-oss-process=image/waterma

2021-06-22 15:15:11 110

原创 MySQL查询语句优化

嵌套查询优化  使用嵌套查询时,MySQL会生成临时表存储子查询的结果,外查询再从临时表读取数据,查询完毕后再删除临时表。  使用JOIN语句代替嵌套查询可以提升查询效率。  优化前:SELECT * FROM table1 WHERE field NOT IN (SELECT field FROM table2)  优化后:SELECT * FROM table1 t1 LEFT JOIN table2 t2 ON t1.t2_id = t2.id WHERE t2.filed IS NU.

2021-06-21 22:39:55 423

原创 ReentrantReadWriteLock读写锁

读写锁的基本原理  读写锁的类层次结构:  使用读写锁时,是获取读锁和写锁,然后根据需要分别调用lock()/unlock()。  从读写锁的构造函数可以看到读锁和写锁公用一个Sync实例。  在ReentrantLock中把state变量拆分成两部分,低16位记录写锁,高16位记录读锁。因为一次CAS操作无法同时操作两个int变量,所以把state拆分,用一个变量记录两种锁状态。通过sharedCount(state)和exclusiveCount(state)可以判断是读进程还是写进程持有锁

2021-06-02 00:11:57 217 1

原创 IO模型

阻塞IO模型  在用户进程请求IO时,调用系统调用recfrom,内核开始准备数据,用户进程阻塞,当数据准备好了内核将数据拷贝到用户内存,返回结果,然后用户进程接触阻塞。非阻塞IO模型  用户进程请求读IO时,如果内核数据没有准备好,不会阻塞用户进程,而是会返回一个error,用户收到该错误消息后,知道数据还没准备好,又再次发起请求,直到数据准备好以后,阻塞用户进程,然后将数据拷贝到用户内存,再正常返回。多路复用IO模型  多个进程的IO注册到一个复用器(Seletor)上,Selector

2021-05-29 15:08:15 71

原创 HTTP/1.1、HTTP/2、HTTP/3对HTTP的改进

HTTP/1.1的改进HTTP/1.0的缺点  1、HTTP/1.0每一次请求都要建立一次连接,然后断开连接,系统开销大。  2、发送方每次都要等上次发出的请求得到响应了才能发送下一个请求,效率太低。HTTP/1.1的改进  1、TCP连接使用长连接,减少频繁建立连接释放连接的系统开销。  2、支持管道进行网络传输,不用等上一个请求被响应就能发送下一个请求,提升效率。HTTP/2的改进HTTP/1.1的性能缺陷  1、请求/响应的头部过大,会增大延迟,要减小延迟只能压缩请求/响

2021-05-29 10:25:42 634

原创 HTTPS加密

HTTPS的加密方式  HTTPS的加密方式采用共享秘钥和公开秘钥混合的混合加密方式。  共享秘钥要求双方使用同一个秘钥,在网络上直接传输共享秘钥,如果被窃听到了,那么加密的内容就会被其他人解密。使用公开秘钥的方式虽然能实现安全传输,但是因为公开秘钥加密比较复杂,传输较慢。所以结合两种加密方式的优缺点,HTTPS使用公开秘钥对共享秘钥加密传输,双方约定好共享秘钥后再用共享秘钥加密通信内容。公开密钥的安全性证书  公开秘钥在传输时,有被替换的风险,为了保证接收方收到的公开秘钥的真实性会由数字证书

2021-05-29 10:25:19 100

原创 HashMap原理

HashMap的底层结构  在JDK7中HashMap是通过“数组+链表”实现的,而JDK8中是通过“数组+链表+红黑树”实现的。数组作为哈希运算后的地址,链表和红黑树是为了解决哈希冲突,将哈希运算后相同的元素放到同一个哈希桶中。HashMap的扩容机制  HashMap数组的初始容量是16,扩容时以2的倍数扩容。是否要扩容由扩容因子决定,默认的扩容因子是0.75,当数组中的元素个数达到容量的0.75就会进行扩容。每个数组中的链表长度达到8时,就会将链表转换为红黑树,在转换前还会检查数组大小是.

2021-05-29 08:27:24 202 1

原创 AQS(队列同步器)

  AQS称作队列同步器(AbstractQueuedSynchronizer)。实现一把具有阻塞和唤醒功能的锁需要的核心要素  1、需要一个state变量标记锁的状态,该变量起码有0和1两个状态,对state操作需要用CAS确保线程安全。在源码中,ASQ类中定义了一个volatile 的state属性。  2、记录哪个线程持有锁。在源码中,AQS的父类AbstractOwnableSynchronizer中定义了一个transient Thread exclusiveOwnerThread属性来

2021-05-28 17:07:54 148

原创 Striped64与LongAdder

LongAdder  在AtmoicLong中,多个线程对变量进行CAS操作的话,每次自旋只有一次线程能修改成功,在并发量高的条件下,效率还是有些不够。为了提高性能,LongAdder将一个变量拆分成多个Cell,每个Cell都有一个Long变量,在并发量大的时候,不同的线程先将变量的计算分摊到多个Cell上,将所有Cell的值加起来就是最终的值。LongAdder只保证了最终一致性  因为在对Cell求和的时候是没有上锁的,所以有可能求和时有别的线程对Cell中的值进行修改,求和得出的结果并不是

2021-05-28 12:42:16 123

原创 ABA问题与解决

ABA问题  CAS是基于值做比较的,如果一个线程将一个值从A改为B,又从B改为A,那么CAS会认为该值没有被修改过。也就是说ABA问题使我们无法判断出一个值是否经过修改,在某些业务场景下就会出现问题。解决方法  我们无法判断一个值是否修改过是因为该值前后是一样的,我们可以通过一个版本号来比较。AtomicStampedReference就是带有版本号的CAS。为什么没有AtmoicStampedInteger或AtmoicStampedLong  因为CAS没有办法同时比较两个变量,在带有版

2021-05-28 12:23:47 659

原创 MySQL事务日志

redo log  重做日志记录实事务操作引起的数据变化,记录数据页的物理修改。InnoDB更新数据时,先将更新记录写到重做日志中,等到系统空闲时再写到磁盘中。buffer pool  buffer pool是在内存中分配的一个区域,作为数据库的缓冲。读取数据时会先在缓冲池中查找,没有再去磁盘检索,写入时,先写入缓冲池,缓冲池里修改的数据会定期刷到磁盘。  redo log 的写入过程如下:binlog  binlog记录数据库的修改逻辑,使数据库有归档能力,能进行数据恢复或备份。redo

2021-05-27 20:56:56 80

原创 数据库与缓存双写一致性

一致性  强一致性:系统写入的数据可以立即被读到,用户体验好,但是系统性能影响大。  弱一致性:不能保证什么时候能读到写入的值,只是尽可能在某段时间后能达到一致的状态。  最终一致性:系统保证在一定时间内能达到一致性状态。经典缓存模式Cache-Aside  读数据时,先查找缓存,如果缓存中有对应的数据直接返回,如果没有就到数据库查找然后更新到缓存中,再返回数据。  写数据时,先更新数据库,然后删除缓存。Read-Throught/Write-Throught  这种模式下读写类似

2021-05-27 18:57:44 357 1

原创 TCP拥塞控制

为什么要拥塞控制   网络出现拥堵时可能会导致数据包延时、丢失,会触发重传机制,这时网络中的包更多了,网络负担更大了,会进入恶性循环。拥塞窗口  拥塞窗口cwnd是发送方维护的一个随网络变化的动态变量,它可以约等于接收窗口rwnd,发送窗口swnd的值为min(拥塞窗口,接收窗口)。  变换规则:网络中没有拥塞,则窗口增大,否则减小。怎么知道是否拥塞  出现超时重传就可以认为出现拥塞拥塞控制算法拥塞发生前慢启动  当发送方没收到一个ACK,拥塞窗口大小就+1。  慢启动门限

2021-05-25 23:09:28 129

原创 流量控制

  发送方发送数据需要考虑接收方的处理能力,如果一直发数据给对方,对方又处理不过来,就会触发很多次重传,导致流量浪费。另外,在通信时接收方如果数据处理不过来会将数据包暂存在缓冲区中,如果缓冲区满了发送方继续向接收方发送数据的话会导致数据包丢失,TCP提供流量控制机制使发送方根据接收方的接收能力控制发送的数据量。滑动窗口  流量控制是基于滑动窗口的。滑动窗口能够告诉发送方接收方还能接收多少数据。  滑动窗口还有另一个重要的作用:TCP每发送一个数据都要进行一次确认应答,如果在收到应答后再发送下一个数据

2021-05-25 21:12:40 279

原创 TCP重传机制

超时重传  发送方在发送数据时,会设定一个计时器,在指定时间内没有收到接收方的应答就会重发该数据。  超时时间RTO的设置:超时时间较大,重发偏慢,效率低,性能差超时时间偏小,数据包没有丢失就重发,重发得频繁,给网络增加拥塞,导致更多超时,恶性循环。  所以超时时间的设置非常重要,超时时间应该略大于往返的时间RTT,由于网络是经常变化的,所以报文往返时间也是变化的,因此RTO是一个动态变化的值。快速重传  快速重传不以时间为驱动,以数据为驱动。图片来自公众号:小林coding  在数

2021-05-25 17:26:44 1165

原创 TCP四次挥手

四次挥手过程图片来自公众号:小林coding  第一次挥手:客户端要关闭连接时,向服务端发送一个FIN报文,FIN位置为1,此时客户端没有数据要发送了,客户端进入FIN_WAIT_1状态。  第二次挥手:服务端收到报文后,向客户端发送ACK报文,服务端进入CLOSE_WAIT。  第三次挥手:服务端在断开连接前可能还有数据要处理,在处理完数据后向客户端发送FIN报文,服务端没有要处理的数据了,进入FIN_WAIT_2状态。  第四次挥手:客户端收到服务端的FIN后向客户端发送一个ACK报文,进入

2021-05-25 11:49:25 90

原创 TCP三次握手

TCP三次握手  开始握手前服务端和客户端都处于CLOSED状态。服务端先监听某个端口处于LISTEN状态。  第一次握手:客户端先生成随机的初始化序列号,把它放在TCP首部的序列号字段中,将SYN标志设为1,。把这个SYN报文发送给服务端,这是还未建立连接报文中没有应用层数据,客户端进入SYN-SENT状态。  第二次握手:服务端接收到客户端的SYN报文后,首先也随机初始化自己的序列号,将序列号放在序列号字段中,把SYN和ACK都置为1,确认应答号放入客户端的序列号+1,把该报文发送给客户端,同样不

2021-05-25 10:50:56 89

原创 信号量Semaphore

  信号量可以看做是对锁的扩展,锁限制了一次只有一个线程能获取共享资源,而信号量却可以使多个线程访问同一资源。Semaphore的构造函数有: &emsppublic Semaphore(int permits); &emsppublic Semaphore(int permits, boolean fair); &emsppermits指定最多能有多少个线程访问信号量,fair是指定是否公平。信号量的逻辑方法:public void acquire(); 获取资源,可响

2021-05-24 22:18:33 63

原创 ReentrantLock重入锁

重入锁的概念  ReentrantLock的意思为重入锁,重入锁完全可以替代synchronized。  ReentrantLock与synchronized相比,它有显示的上锁释放锁的操作过程,所以比Synchronized灵活很多。上锁、释放锁的方式:public ReentrantLock lock = new ReentrantLock();lock.lock();//上锁lock.unlock();//释放锁  为什么叫重入锁呢,因为对于同一个线程来说,只要获取到重入锁那么就可以重

2021-05-24 21:37:23 310

原创 Redis缓存穿透、缓存击穿、缓存雪崩

缓存穿透  key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。  原因:1、Redis查询不到,直接访问数据库,而数据库也没有相关数据;2、出现大量非正常url访问。解决方案1、 对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟。2

2021-05-23 15:29:52 106

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除