synchronize锁详解

这篇具有很好参考价值的文章主要介绍了synchronize锁详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、什么是synchronize?

2、为什么要用synchronize锁?

2.1 代码演示

2.2 原因分析

2.3 专有名词解释

2.3.1 临界资源

2.3.2 临界区

2.3.3 竞态条件

3、synchronize锁的原理

3.1 锁升级过程

3.1.1 偏向锁

3.1.2 轻量级锁

3.1.3 重量级锁

3.1.4 总体过程

3.2 锁优化

3.2.1 自旋锁

3.2.2 锁粗化

3.2.3 锁消除

4. synchronize的基本使用

4.1 使用方式

4.1.1 synchronize锁某个对象

4.1.2 synchronize锁某个类

4.1.3 synchronize锁某个类的成员方法

4.1.4 synchronize锁住某个类的静态方法

         4.1.5 wait/notify


1、什么是synchronize?

官方:同步方法支持一种简单的策略来防止线程受到干扰和内存一致性错误;如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成

通俗点来说就是程序中用于保护线程安全的一种机制。 

2、为什么要用synchronize锁?

2.1 代码演示

public class Test19 {
    public static int a=0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程1对a进行50000次a++
                    a++;
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程2对a进行50000次a--
                    a--;
                }
            }
        });
        t1.start();
        t2.start();
        //等待t1 t2线程运行完后输出a的值
        t1.join();
        t2.join();
        System.out.println(a);
    }
}

按理来说,a最后输出的值应该是0,但是大家在运行上述代码后会发现结果并不是确定的 。可见,这里在多线程对共享变量操作的情况下发生了线程安全问题。

但是呢,如果我们给线程加上synchronize锁后,会发现最后的值永远是0,代码如下:

public class Test19 {
    public static int a=0;
    public static Object object=new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程1对a进行50000次a++
                    synchronized (object){ //加上synchronize锁
                        a++;
                    }
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程2对a进行50000次a--
                    synchronized (object){ //加上synchronize锁
                        a--;
                    }
                }
            }
        });
        t1.start();
        t2.start();
        //等待t1 t2线程运行完后输出a的值
        t1.join();
        t2.join();
        System.out.println(a);
    }
}

2.2 原因分析

首先大家先要知道,我们编程语言中对于a++或者a--这个操作来说,并不是原子性的,而是在编译后会被分别翻译成如下四条指令:

a++:

getstatic a  // 获取静态变量a 的值
a const_1 // 准备常量 1
a add // 自增
putstatic a  // 将修改后的值存入静态变量a

a--:

getstatic a // 获取静态变量a的值

a const_1 // 准备常量 1
a sub // 自减
putstatic a  // 将修改后的值存入静态变量a

可见,对于多线程情况下会出现线程安全问题的原因也很好理解:比如我第一个线程t1进行a++,第二个线程t2进行a--,此时t1取出了a的值0,同时t2也取出了a的值,由于此时t1只是取出a的值,并没有对a进行操作并且赋值到原来的地址上,因此t2取出a的值也为0,然后接下来t1准备常量1,然后让a+=常量,再赋值,此时a变为1。同理,t2也进行准备常量1,然后对a-=常量,由于t2取出的a是t1赋值之前的,因此t2再对a操作后,a的值变为了-1 !

这不就出现线程安全了吗!原本两个线程正常情况下对a操作后应该是a为0,但是此时a为-1!以上例子简单的解释了一下为什么多线程情况下对共享变量进行操作会有线程安全问题。

当然,上述例子只是某一种情况,也有可能是t2先操作完,t1再操作,因此最后值为1,大家可以自行去思考有多少种情况。

总结:线程安全问题是由于多线程在临界区内对临界资源同时访问发生了竞态条件导致的。

那么?什么是临界区?什么是临界资源?什么又是竞态条件?我们接下来给大家一一解释。

2.3 专有名词解释

2.3.1 临界资源

临界资源:一次只允许一个线程访问的资源称为临界资源 。

2.3.2 临界区

 临界区:线程对于访问临界资源的代码块称为临界区。

2.3.3 竞态条件

竞态条件:多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件。

3、synchronize锁的原理

大家看完上文肯定知道了synchronize锁能保证多线程对于临界资源的访问不会发生线程安全问题,那么它的原理是怎么样的呢?

Java对象头:由Mark word和klasspointer两部分组成,如果是数组,还包括数组长度

我们主要来看看Mark word这一部分,以下两个图分别是32位虚拟机和64位虚拟机的Mark word结构

32位虚拟机:

synchronize锁详解

64位虚拟机:

synchronize锁详解

解释:

后两位表示状态

State代表对象状态 Normal是正常状态,Biased是偏向锁状态,Lightweight Locked是轻量级锁状态,Heavyweight Locked是重量级锁状态,Marked for GC指被JVM垃圾回收。以上从Biased->Lightweight Locked->Heavyweight Locked是我们接下来要说的synchronize锁升级过程,大家看不懂可以先记着。

3.1 锁升级过程

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁    // 随着竞争的增加,只能锁升级,不能降级

3.1.1 偏向锁

偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程之后重新获取该锁不再需要同步操作:

当锁对象第一次被线程获得的时候进入偏向状态,标记为 101,同时使用 CAS 操作将线程 ID 记录到 Mark Word。如果 CAS 操作成功,这个线程以后进入这个锁相关的同步块,查看这个线程 ID 是自己的就表示没有竞争,就不需要再进行任何同步操作

我们从上图State为Biased状态的Mark word结构可以看出,有一个thread字段,这个是用来存储对该对象加锁的线程的信息。

总的来说偏向锁就是:某个对象第一次被某线程加锁时,会将自己对象头上的Mark word里面的东西改为我们上图说的State为Biased状态的东西,所以当下次同一线程再次来加锁时,发现这个对象Mark word里面存的线程是自己,那就不用去进行一系列加锁流程了,能够提高效率。

但是当此时另一个线程来加锁时,发现这个对象在此之前已经成为偏向其他线程了,那么就会进行锁升级的过程,升级为轻量级锁。

3.1.2 轻量级锁

轻量级锁:一个对象有多个线程要加锁,但加锁的时间是错开的(没有竞争),可以使用轻量级锁来优化。

当某个对象对应的锁升级为轻量级锁时,会将自己Mark word状态转为State对应的Lightweight Locked那一行,此时会发生如下过程:

①创建锁记录(Lock record)对象,每个线程的栈帧都会包含一个锁记录的结构,存储锁定对象的 Mark Word。(锁记录对象结构图如下)

synchronize锁详解可见,锁记录对象包含了两部分:锁记录对象地址和对象引用部分。

②让锁记录中 Object reference 指向锁住的对象,并尝试用 CAS 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录。

此时会发生两种情况:

2.1 CAS成功:

如果 CAS 替换成功,对象头中存储了锁记录地址和状态 00(轻量级锁) ,表示由该线程给对象加锁。

synchronize锁详解

2.2 CAS失败:说明此时对象已经被某个线程加锁了,此时又分为两种情况:

第一种情况:是同一线程对对象加锁。

由于我们知道synchronize是可重入锁,因此会发生如下过程:线程发现自己执行了 synchronized 锁重入,就添加一条 Lock Record 作为重入的计数

synchronize锁详解

可重入锁:指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
synchronized 和 ReentrantLock 都是可重入锁。

第二种情况:不是同一线程加锁。

如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程。

锁膨胀:在尝试加轻量级锁的过程中,CAS 操作无法成功,可能是其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁

③锁解锁过程

  • 当退出 synchronized 代码块(解锁时)

    • 如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减 1

    • 如果锁记录的值不为 null,这时使用 CAS 将 Mark Word 的值恢复给对象头

      • 成功,则解锁成功

      • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

3.1.3 重量级锁

在说synchronize升级为重量级锁是如何保证线程安全问题之前,我们先来说一个东西:Monitor

Monitor 被翻译为监视器或管程

每个 Java 对象都可以关联一个 Monitor 对象,Monitor 也是 class,其实例存储在堆中,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针,这就是重量级

工作流程:

  • 开始时 Monitor 中 Owner 为 null

  • 当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor 中只能有一个 Owner,obj 对象的 Mark Word 指向 Monitor,把对象原有的 MarkWord 存入线程栈中的锁记录中(轻量级锁部分详解)  

 synchronize锁详解 

  • 在 Thread-2 上锁的过程,Thread-3、Thread-4、Thread-5 也执行 synchronized(obj),就会进入 EntryList BLOCKED(双向链表)

  • Thread-2 执行完同步代码块的内容,根据 obj 对象头中 Monitor 地址寻找,设置 Owner 为空,把线程栈的锁记录中的对象头的值设置回 MarkWord

  • 唤醒 EntryList 中等待的线程来竞争锁,竞争是非公平的,如果这时有新的线程想要获取锁,可能直接就抢占到了,阻塞队列的线程就会继续阻塞

  • WaitSet 中的 Thread-0,是以前获得过锁,但条件不满足进入 WAITING 状态的线程(wait-notify 机制)

 synchronize锁详解 

注意:

  • synchronized 必须是进入同一个对象的 Monitor 才有上述的效果

  • 不加 synchronized 的对象不会关联监视器,不遵从以上规则

3.1.4 总体过程

可见当我们使用synchronize对某个对象上锁时多线程访问过程如下:

某个对象第一次被某个线程上锁时,会从无锁(Normal)状态升级为Biased(偏向锁状态),接下来该线程再对该对象上锁时,可以直接访问。

(此时仅有一个线程对该对象上锁,所以可以理解成偏向锁是在只有一个线程对某个对象上锁时出现)

当其他线程来对该对象上锁时,发现这个对象是偏向其他线程,因此会升级成轻量级锁,在解锁后该对象会变回无锁状态或者偏向锁状态(这个和批量重定向、批量撤销有关)。

(此时虽然有多个线程对该对象上锁,但是他们并没有发生竞争,因此可以理解成轻量级锁是在有多个线程上锁,但是没有发生竞争的情况下出现)

  • 批量重偏向:当撤销偏向锁阈值超过 20 次后,JVM 会觉得是不是偏向错了,于是在给这些对象加锁时重新偏向至加锁线程。

  • 批量撤销:当撤销偏向锁阈值超过 40 次后,JVM 会觉得自己确实偏向错了,根本就不该偏向,于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的。

当多个线程对该对象加锁,并且发生竞争时,会升级为重量级锁。

(此时有多个线程对该对象上锁,并且发生了竞争,因此可以理解成重量级锁是发生在有竞争的情况下)

注意事项

一个对象创建时:

  • 如果开启了偏向锁(默认开启),那么对象创建后,MarkWord 值为 0x05 即最后 3 位为 101,thread、epoch、age 都为 0

  • 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟。JDK 8 延迟 4s 开启偏向锁原因:在刚开始执行代码时,会有好多线程来抢锁,如果开偏向锁效率反而降低

  • 当一个对象已经计算过 hashCode,就再也无法进入偏向状态了

  • 添加 VM 参数 -XX:-UseBiasedLocking 禁用偏向锁

撤销偏向锁的状态:

  • 调用对象的 hashCode:偏向锁的对象 MarkWord 中存储的是线程 id,调用 hashCode 导致偏向锁被撤销(这里很简单理解,大家去看看上面那个图,只有State状态为Normal,才有hashcode)

  • 当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁

  • 调用 wait/notify,需要申请 Monitor(只有重量级锁才有monitor),进入 WaitSet

3.2 锁优化

从上述synchronize的上锁过程大家可以发现是比较复杂、繁琐的,因此JVM对synchronize锁进行了一系列的锁优化操作。

3.2.1 自旋锁

重量级锁竞争时,尝试获取锁的线程不会立即阻塞,可以使用自旋(默认 10 次)来进行优化,采用循环的方式去尝试获取锁

注意:

  • 自旋占用 CPU 时间,单核 CPU 自旋就是浪费时间,因为同一时刻只能运行一个线程,多核 CPU 自旋才能发挥优势

  • 自旋失败的线程会进入阻塞状态

优点:不会进入阻塞状态,减少线程上下文切换的消耗

缺点:当自旋的线程越来越多时,会不断的消耗 CPU 资源

3.2.2 锁粗化

对相同对象多次加锁,导致线程发生多次重入,频繁的加锁操作就会导致性能损耗,可以使用锁粗化方式优化

如果虚拟机探测到一串的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部

  • 一些看起来没有加锁的代码,其实隐式的加了很多锁:

public static String concatString(String s1, String s2, String s3) {
    return s1 + s2 + s3;
}
  • String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,转化为 StringBuffer 对象的连续 append() 操作,每个 append() 方法中都有一个同步块

public static String concatString(String s1, String s2, String s3) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    sb.append(s3);
    return sb.toString();
}

扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,只需要加锁一次就可以。

3.2.3 锁消除

锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除,这是 JVM 即时编译器的优化锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除(同步消除:JVM 逃逸分析)

4. synchronize的基本使用

4.1 使用方式

synchronize可以锁静态方法、成员方法、某个对象、某个类。

4.1.1 synchronize锁某个对象

public class Test20 {
    public static int a=0;
    public static myObject object=new myObject();
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程1对a进行50000次a++
                    synchronized (object){ //给object对象加上synchronize锁
                        a++;
                    }
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程2对a进行50000次a--
                    synchronized (object){ //给object对象加上synchronize锁
                        a--;
                    }
                }
            }
        });
        t1.start();
        t2.start();
        //等待t1 t2线程运行完后输出a的值
        t1.join();
        t2.join();
        System.out.println(a);
    }
}
class myObject{
    public static void method1(){
        System.out.println("加在静态方法上");
    }
    public void method2(){
        System.out.println("加在成员方法上");
    }
}

可见,以上是给object这个对象加锁,保证了每次只有一个线程能访问synchronize锁住的代码块,因此线程安全。

4.1.2 synchronize锁某个类

public class Test20 {
    public static int a=0;
    public static myObject object=new myObject();
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程1对a进行50000次a++
                    synchronized (myObject.class){ //给myObject类加上synchronize锁
                        a++;
                    }
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++) { //线程2对a进行50000次a--
                    synchronized (myObject.class){ //给myObject类加上synchronize锁
                        a--;
                    }
                }
            }
        });
        t1.start();
        t2.start();
        //等待t1 t2线程运行完后输出a的值
        t1.join();
        t2.join();
        System.out.println(a);
    }
}
class myObject{
    public static void method1(){
        System.out.println("加在静态方法上");
    }
    public void method2(){
        System.out.println("加在成员方法上");
    }
}

4.1.3 synchronize锁某个类的成员方法

class myObject{
    public static void method1(){
        System.out.println("加在静态方法上");
    }
    public synchronize void method2(){
        System.out.println("加在成员方法上");
    }
}

这种其实就等价于给对象加锁,因为我们synchronize锁有竞争时是需要创建monitor的,每个类或者每个对象对应唯一monitor,因为成员方法属于对象,因此给成员方法加锁实际上就是给对象加锁。

4.1.4 synchronize锁住某个类的静态方法

class myObject{
    public synchronize static void method1(){
        System.out.println("加在静态方法上");
    }
    public void method2(){
        System.out.println("加在成员方法上");
    }
}

这种实际上等价于给类加锁,因为静态方法属于类,因此给静态方法加锁实际上就是给类加锁。

注意,monitor是和对象或者类一一对应的,类的monitor和对象的monitor是不同的monitor,比如上述myObject类对应一个monitor,而myObject类不同的对象对应不同的monitor。

4.1.5 wait/notify

需要获取对象锁后才可以调用 锁对象.wait(),notify 随机唤醒一个线程,notifyAll 唤醒所有线程去竞争 CPU

Object 类 API:

public final void notify():唤醒正在等待对象监视器的单个线程。
public final void notifyAll():唤醒正在等待对象监视器的所有线程。
public final void wait():导致当前线程等待,直到另一个线程调用该对象的 notify() 方法或 notifyAll()方法。
public final native void wait(long timeout):有时限的等待, 到n毫秒后结束等待,或是被唤醒

说明:wait 是挂起线程,需要唤醒的都是挂起操作,阻塞线程可以自己去争抢锁,挂起的线程需要唤醒后去争抢锁

对比 sleep():

  • 原理不同:sleep() 方法是属于 Thread 类,是线程用来控制自身流程的,使此线程暂停执行一段时间而把执行机会让给其他线程;wait() 方法属于 Object 类,用于线程间通信

  • 锁的处理机制不同:调用 sleep() 方法的过程中,线程不会释放对象锁,当调用 wait() 方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池(不释放锁其他线程怎么抢占到锁执行唤醒操作),但是都会释放 CPU

  • 使用区域不同:wait() 方法必须放在同步控制方法和同步代码块(先获取锁)中使用,sleep() 方法则可以放在任何地方使用

底层原理:

  • Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态

  • BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片

  • BLOCKED 线程会在 Owner 线程释放锁时唤醒

  • WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,唤醒后并不意味者立刻获得锁,需要进入 EntryList 重新竞争

synchronize锁详解文章来源地址https://www.toymoban.com/news/detail-430780.html

到了这里,关于synchronize锁详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python接口自动化(二)--什么是接口测试、为什么要做接口测试(详解)

    上一篇和大家一起科普扫盲接口后,知道什么是接口,接口类型等,对其有了大致了解之后,我们就回到主题-接口测试。 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的

    2024年02月10日
    浏览(45)
  • 编程开发8大语言详解,为什么Java是我最推荐的?

    很多没有接触过编程语言的同学,都会觉得编程开发特别高端和神奇,担心理解不了更担心学不会。 当然,也有人会认为,你既然是做编程的,那么你应该什么都会,什么软件的开发都能完成,这是平哥经常听到的两种声音。 在此,平哥需要给大家科普一下, 编程确实改变

    2024年02月05日
    浏览(53)
  • SSL/TLS协议详解 - https为什么比http更安全

    SSL/TLS是世界上应用最广泛的密码通信方法。比如,在网上商城输入信用卡卡号时,Web浏览器就会使用SSL/TLS进行密码通信。使用SSL/TLS可以对通信对象进行认证,还可以确保通信内容的机密性。TLS相当于SSL的后续版本。 SSL (Secure Sockets Layer)安全套接层协议 :由Netscape公司开发

    2024年02月05日
    浏览(37)
  • 【C/C++】详解程序环境和预处理(什么是程序环境?为什么要有程序环境?如何理解程序环境?)

    目录 一、前言 二、 什么是程序环境? 三、 为什么要有程序环境? 四、如何理解程序环境? 🍎 ANSI C 标准  🍐 翻译环境和执行环境  五、详解翻译环境和执行环境  🍇翻译环境(重点!!)  💦编译环境(预处理---编译---汇编)  💦链接环境(链接)  🍉执行环境

    2024年02月21日
    浏览(43)
  • 预编译为什么能防止SQL注入?一看你就明白了。预编译原理详解

    「作者主页」: 士别三日wyx 「作者简介」: CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」: 对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 先简单了解一下SQL注入的过程。 比如一个查询功能,根据用户输入的id,查

    2024年02月07日
    浏览(37)
  • 【linux】/etc/security/limits.conf配置文件详解、为什么限制、常见限制查看操作

    /etc/security/limits.conf 是一个用于配置用户或用户组 资源限制 的配置文件。这个文件通常用于 设置系统资源的软限制和硬限制 ,以及一些特殊权限。 下面是一些 /etc/security/limits.conf 文件中可能包含的常见配置项: 描述 第一列表示用户和组(@开头),这里的 * 代表所有用户。

    2024年02月03日
    浏览(30)
  • DHCP协议详解,报文内容,如何查看报文,为什么offer报文会以广播的形式进行发送

    dhcp地址分配协议,目前有dhcpv4以及dhcpv6,分别作用于ipv4与ipv6的网络中。 主要作用:dhcp服务端通过dhcp协议下发ip地址给到客户端(pc,终端),使得pc能有上网的能力。 1.dhcp交互图 2.dhcp报文交互过程中,有大部分的报文都是广播报文。 客户端拥有ip前,是可以接收所有的广

    2024年02月06日
    浏览(23)
  • Netty为什么高效,为什么这么受欢迎?

    上篇文章通过 Java NIO 的处理流程与 Netty 的总体流程比较,并结合 Netty 的源码,可以更加清晰地理解Netty。本文将结合源码详细解析Netty的高效和强大功能的设计原理,学习 Netty 是如何实现其卓越的性能和功能特性,也希望可以在日后工作中利用到 Netty 的设计思想。 我们先看

    2024年02月12日
    浏览(57)
  • Redis—Redis介绍(是什么/为什么快/为什么做MySQL缓存等)

    一、Redis是什么 Redis 是一种 基于内存的数据库 ,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于 缓存,消息队列、分布式锁等场景 。         Redis 提供了多种数据类型来支持不同的业务场景,比如 String(字符串)、Hash(哈希)、 List (列表)、Set(集合)、

    2024年02月10日
    浏览(52)
  • AIMD 为什么收敛(tcp reno/cubic 为什么好)

    TCP 拥塞控制目标是缓解并解除网络拥塞,让所有流量公平共享带宽,合在一起就是公平收敛。 AIMD(几乎所有与拥塞控制相关的协议或算法都有 AIMD 的影子,包括 RoCE,BBRv2) 为什么收敛?我一般会给出下面的老图: 虽然只展示了两条流的收敛,但 n 条流收敛的展示无非就是将

    2024年02月06日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包