JUC系列(一&二):线程基础、生产与消费模型、8锁问题

华为OD机试真题 2026.11.4. 新系统 【河流水系流量评估】 华为OD机试真题 新系统【河流水系流量评估】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【河流水系流量评估】多语言题解。 阅读详情

发现 学习狂神JUC系列少了开篇 特此补发

线程与进程

线程、进程、如何来解释

进程 : 一个程序 如 QQ.exe Music.exe 程序的集合

一个进程可以包含多个线程,至少包含一个线程

Java 默认是开启两个线程 main GC

线程: 开了一个进程 比如: typora 写字,自动保存(线程负责的)

对于Java 而言创建线程我们学习到的方法有三种 : Thread , Runnable , Callable

PS :Java本身是无法开启线程的!!!

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

	// 本地方法 调用底层的 C++ Java本身不能直接操作硬件
    private native void start0();

并发,并行

我们要学习并发编程,首先要了解 : 并发和并行是什么

并发(多线程操作同一资源)

  • CPU一核,模拟出来多条线程,天下武功,唯快不破,快速交替。

并行(多个程序一起走)

  • CPU 多核,多个线程同时执行;如果需要提高性能 : 线程池

查看一下自己的cpu核数

image-20220301201453940

并发编程的本质:充分利用CPU的资源

所有的公司都很看重,就是效率,比如裁员:一个厉害的可以大于两个不再那么厉害的甚至更多

比如: 做事情的速度 高手:1s,一般人: 10s 肯定是 1s 的更加的之前也更加的有效率;

做到人员(减少),但是技术力却提高了

线程有几个状态

通过Thread 源码里的枚举类:State 中的属性可以看出来

线程有六个状态

    public e
num State {
        //新的线程
        NEW,
        //运行中
        RUNNABLE,
        // 阻塞
        BLOCKED,
        //等待,一直等
        WAITING,
        //等待一段时间就不等了
        TIMED_WAITING,
        // 终止线程
        TERMINATED;
    }

wait/sleep的区别

1、他们来自不同的类

wait => object

sleep => Thread

2、关于锁的释放

wait会释放锁,sleep不会释放锁

抽象理解 : sleep 睡着了没办法释放, wait 是等待,有人需要的释放

wait必须在同步代码块中使用,得有需要等的人

sleep可以在任何地方使用

3、是否需要捕获异常

wait 不需要捕获异常

sleep 需要捕获异常

Lock锁

传统 Synchronized

传统火车票案例

/**
 * @projectName: JUC
 * @package: Thread
 * @className: SaleTicketDemo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 20:28
 * @version: 1.0
 */
public class SaleTicketDemo {
    /**
     * 真正的多线程开发 公司中的需要降低耦合性
     * 线程就是一个单独的资源类,没有任何附属的操作
     * 1、 属性。方法
     * */
    public static void main(String[] args) {
        //并发: 多线程操作同一个资源类,把资源丢入线程
        Ticket ticket = new Ticket();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
}

//资源类 OOP
class Ticket {
    //属性 方法
    private int number = 50;

    //    买票的方式
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
        }
    }
}

Lock接口

image-20220301204107904

常用的一些实现类

image-20220301204151242

image-20220301204430613

公平锁 : 十分公平,可以先来后到 比如 前一个线程要执行30s 后面的 需要3s 后者必须等待前者执行完

非公平锁 : 十分不公平,可以根据条件来插队

如何用Lock来编写火车票Demo

  1. new ReentrantLock();
  2. Lock.lock(); 解锁
  3. finally => lock.unlock(); 解锁

Synchronized和Lock区别

  1. Synchronized 内置的java关键字,Lock是一个java类
  2. Synchronized 无法获取锁的状态 Lock 可以判断是否获取到锁
  3. Synchronized 会自动释放锁, Lock 必须手动解锁,如果不释放锁 就会死锁
  4. Synchronized 线程1(获得锁,阻塞),线程2(等待,死等),Lock锁不一定会等待
  5. Synchronized 可重入锁,不可以中断,非公平,Lock 可重入锁,可以判断锁,手动调整
  6. Synchronized 适合锁少量代码同步问题,Lock 适合锁大量的同步代码

锁是什么,如何判断锁的是谁

生产者和消费者问题

生产者和消费者问题:Synchronized 版

面试高频 : 单例模式,排序算法,生产者和消费者 死锁

package ProduceAndconsum;

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: A
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:08
 * @version: 1.0
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data {
    private int number = 0;

    //    +1
    public synchronized void increment() throws InterruptedException {
        if (number != 0) {
            //    等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        //    通知其他线程 +1 完毕了
        this.notifyAll();
    }

    //    -1
    public synchronized void decrement() throws InterruptedException {
        if (number == 0) {
            //    等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        //    通知其他线程 -1 完毕了
        this.notifyAll();
    }
}

但是,写出来这个简单的模型 面试官还是会挑的出毛病来,问题出在那?

问题,现在我们是两个线程 ,我们加到四个线程 A,B,C,D 现在还是安全的吗?答案是肯定的不是

虚假唤醒问题:我们增加到四个线程输出的时候就会发现一些问题来,输出不再是0101了

image-20220301212425229

image-20220301212149214

这里如何解决呢? 将 if 换成 while循环

修改之后根据官方文档的解释之后,将if改编成while

image-20220301212605922

输出就又回到了正常

package ProduceAndconsum;

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: A
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:08
 * @version: 1.0
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data {
    private int number = 0;

    //    +1
    public synchronized void increment() throws InterruptedException {
        while (number != 0) {
            //    等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        //    通知其他线程 +1 完毕了
        this.notifyAll();
    }

    //    -1
    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            //    等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        //    通知其他线程 -1 完毕了
        this.notifyAll();
    }
}

生产者和消费者问题: JUC版

在新的学习中 synchronized 有 Lock 替换

那么 wait 和 notify谁来替换呢?

image-20220301212829262

通过 Lock来找到 Condition

image-20220301213020385

image-20220301213059259

package ProduceAndconsum;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: B
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:31
 * @version: 1.0
 */


public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data2 {
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //等待
    //    condition.await();
    //唤醒全部
    //    condition.signalAll();

    //    +1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                //    等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 +1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //    -1
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number == 0) {
                //    等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 -1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
package ProduceAndconsum;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: B
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:31
 * @version: 1.0
 */


public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data2 {
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //等待
    //    condition.await();
    //唤醒全部
    //    condition.signalAll();

    //    +1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                //    等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 +1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //    -1
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number == 0) {
                //    等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 -1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

任何的一个新技术,都不可能是单纯的去替代老的技术,一定有优化和补充

Condition

image-20220301214146773

我们更换完代码之后,可以正常输出但是还是混乱的,我们想要达到 A->B->C->D 这样输出,这个时候就引出了新的知识点

Conddition精准唤醒

package ProduceAndconsum;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: B
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:31
 * @version: 1.0
 */


public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data3 {
    private int number = 1;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();

    public void printA() {
        lock.lock();
        try {
            //    业务,判断 执行 通知
            while (number != 1) {
                //等待
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>AAA");
            number = 2;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            //    业务,判断 执行 通知
            while (number != 2) {
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBB");
            number = 3;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            //    业务,判断 执行 通知
            while (number != 3) {
                //等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBB");
            number = 1;
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

输出的结果就达到我们的预期了

image-20220301215538935

8锁现象

锁 ----> 刚new 出来的对象、class

8锁就是关于的锁的八个问题,下面也有四个demo类来阐述各种场景下锁的不同状态

demo1

两个问题:

正常模式下 在两个同步线程方法调用的时候 中途延时1s 会不会改变输出结果

答:不会影响输出顺序

正常模式下 同步方法内延时4s 会不会印象输出

答:不会影响输出顺序

  • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话

demo代码

package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 * 1. 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
 * 2. 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo {
    public static void main(String[] args) {
        phone phone = new phone();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "B").start();
    }
}

class phone {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}

demo2

同步方法执行和普通方法执行顺序

答 :普通方法没有锁,所以普通方法先

两个对象执行会不会影响顺序

答:会 两个不同的对象锁也是不同的 ,对象1 还在等待,对象2调用的call方法不用等待所以先输出

  • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
  • 新增两个对象 是先打电话还是先发短信
package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 * 3、 现在新增一个普通方法  问 先发短信还是先发hello 先输出hello
 * 4、 新增两个对象 是先打电话还是先发短信
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo2 {
    public static void main(String[] args) {
        //两个对象,现在是两个调用者所以是两个锁
        phone2 phone = new phone2();
        phone2 phone1 = new phone2();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.call();
        }, "B").start();
    }
}

class phone2 {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }


    //因为这里没有锁,不受锁的影响
    public void hello() {
        System.out.println("hello");
    }
}

demo3

一个对象调用静态同步方法会不会改动执行顺序

答: 不会 ,static是锁的类模版全局唯一,不会改变锁的执行交换顺序

两个对象调用静态同步方法会不会改变执行顺序

答:不会 static修饰的是类模版,锁的也是类模板而不是类对象,只要是这个类生成的对象,不管多少个都不会改变顺序

  • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
  • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 *  5、添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
 *  6、 添加两个对象,增加两个同步方法 先打印  发短信 还是打电话?
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo3 {
    public static void main(String[] args) {

        //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
        // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
        new Thread(() -> {
            Phone3.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Phone3.call();
        }, "B").start();
    }
}

//phone3是唯一的一个class
class Phone3 {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    // 这里 static 是静态方法 ,类一加载就有了,这个用的锁不再是 phone锁 而是class锁
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }

}

demo4

  • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
  • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

答: 不是同一个锁,谁执行快就输出谁,

package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 *  7、一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
 *  8、两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo4 {
    public static void main(String[] args) {
        Phone4 phone = new Phone4();
        //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
        // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
        new Thread(() -> {
            Phone4.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "B").start();
    }
}

//phone3是唯一的一个class
class Phone4 {
    //静态同步方法是锁的 class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    //这里普通同步方法锁的是 phone
    public synchronized void call() {
        System.out.println("打电话");
    }

}

小结

我们通过八种不同的情况来理解锁在不同情况下的执行情况:

  • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
  • 新增两个对象 是先打电话还是先发短信
  • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
  • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
  • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
  • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

大家可以自己写一下代码看一些这些问题的结果,实践出效果,实践出理解

锁的东西,无非就是对象和 类模版

  1. new 出来的对象 就是锁具体的对象,比如普通同步方法
  2. 带有static 修饰的静态同步方法 锁的是类模版是全局唯一的对象class如 : class
ANSYS Maxwell:创建即用型 3D 轴向磁通电机模型 Magnet Length” 是磁体的径向长度。“Magnet Thickness” 是磁体的轴向长度。为了对 AFM 进行更详细的分析,用户需要在 ANSYS Maxwell 中运行 3D FEA 分析。对于 2D 模型,它们类似于 MotorCAD 中的模型,后者对等效的直线电机进行建模。单击“确定”后,将创建一个 3D Maxwell 模型。“长度”是转子铁的轴向长度。然后需要定义定子的“磁芯”尺寸和材料。“长度”是定子铁芯的轴向长度。“转子”# 的极数必须与“定子”中定义的磁极相同。 阅读详情

相关推荐

【Python脚本】Python自动大麦网抢票,准点原价秒杀演唱会门票,拒绝黄牛交易!从我做起!!!1

你是否错过了周杰伦的票,最近又没抢到五月天的票,不要太难过,今晚有张学友的票,后天还有张杰的票,加油哦!

weixin_45841831的博客 6万+

JavaEE多线程入门:从线程安全到线程池的完整实战指南

在Java开发中,多线程并非进阶选项,而是每个程序员从Hello World起就身处其中的基础环境。理解进程与线程的差异、并发三大特性以及synchronized与volatile的核心原理,是解决数据错乱、保证线程安全的根本。线程池作为JavaEE后端最常用的并发设施,通过复用与限流机制,有效应对高并发请求,避免资源耗尽。从生产者消费者模型到多线程下载与断点续传,工程实践处处考验对锁、协作与状态管理的理解。无论是配置vscode javaee语言环境,还是排查死锁问题,扎实的并发基础都是高效开发的底气。本

weixin_29895109的博客 263

CXL ALMP(ARB/MUX Link Management Packet)理解

ALMP(ARB/MUX Link Management Packet) 是CXL协议中由ARB/MUX层生成和处理的专用管理报文,用于协调链路电源状态切换(如L0s/L1)和虚拟链路状态机(vLSM)同步。ALMP是ARB/MUX层的控制平面报文,专用于以下场景:协调多协议(CXL.io、CXL.cachemem等)的链路状态切换(如L1进入/退出)。维护各协议独立的虚拟链路状态机(vLSM) 状态同步。在物理层(PHY)与链路层(Link Layer)之间仲裁电源管理请求。

Summer的博客 1198

剑指Java面试 -- JUC和IO

j.u.c包和io知识点

Carpe Diem. 250

【数据集处理】DroneVehicle数据集处理方法

DroneVehicle数据集由无人机采集的56,878张图像组成,其中一半是RGB图像,其余为红外图像。作者为这五个类别制作了带有定向边界框的丰富注释。

qq_44102942的博客 5045

CUDA编译踩坑实录:如何正确设置-gencode参数适配你的GPU算力

本文深入解析CUDA编译中-gencode参数的核心原理与实战配置,帮助开发者解决常见的'Unsupported gpu architecture'错误。通过厘清虚拟架构与真实架构的区别,并提供多版本兼容的CMake配置方案,确保编译出的代码能精准适配不同GPU算力,兼顾性能与广泛兼容性。

weixin_28829903的博客 338

147.【2024Java八股-全网最全-10w字】

eg: 学校、院校、经验、年龄、跳槽频率…基本包括16种常见使用场景: 、数据共享分布式、、全局 ID、计数器、限流、位统计、购物车、用户消息时间线 timeline、、抽奖、点赞、签到、打卡、商品标签、商品筛选、用户关注、推荐模型、排行榜.详细讲解16种使用场景: https://blog.youkuaiyun.com/zxl646801924/article/details/123202381用户使用id进行查询文章的时候,首先会查询redis数据库,如果在redis中命中数据,那么redis就向用户返回查询的结果

qq_69683957的博客 1736

计算机组成原理实验二 运算器组成实验

事实上,ALU的设计是在全加器的基础上,对全加器功能的扩展来实现符合要求的多种算术/逻辑运算的功能。为了实现多种功能的运算,An、Bn数据是不能直接与全加器相连接的,它们受到功能变量F3—F1的制约,因此,可由An、Bn数据和功能变量F3-F1组合成新的函数Xn 、Yn,然后,再将Xn 、Yn和下一位进位Cn-1通过全加器进行全加运算以实现所需的运算功能。AC寄存器可用来存储ALU的输入以实现两个操作数的运算操作,也可以用来存储来自ALU的结果,以将这些结果反馈给ALU以进行更多的处理。

qq_63574533的博客 1447

【FDE知识库】(1):AI 编程时代开发语言选型分析,云使用go,企业用java,算法python,底层c

AI 编程时代,开发语言选型需综合考量AI适配度、编译速度、跨平台与学习成本。Python以海量语料和即时执行成首选,适用于AI/ML与原型开发;TypeScript凭借类型系统与秒级构建,成为前端及应用层主流;Go以极速编译与统一风格,契合AI代理高效迭代需求。Rust/C++虽性能强,但编译慢、学习成本高,不适于快速试错。综合推荐:优先选Python、TypeScript、Go,兼顾效率与生态,实现最优AI协作体验。

freewebsys的专栏 477

头歌操作系统 课堂练习4.2:页式内存管理 答案 无解析

头歌操作系统 课堂练习4.2:页式内存管理 答案 无解析

kercii的博客 1万+

C# 上位机之海康工业相机开发(SDK)实战教程

海康工业相机的C#开发核心是基于MVS SDK的“初始化→枚举→连接→采集→释放”流程,其中图像回调处理和格式转换是关键。严格按照SDK文档处理设备类型(网口/USB)和像素格式;及时释放图像资源,避免内存泄漏;错误码是排查问题的重要依据(可参考SDK中的“错误码说明”文档)。掌握这些基础后,可结合机器视觉算法(如OpenCV)实现缺陷检测、尺寸测量等高级功能,构建完整的工业视觉系统。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 3825

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 214

天猫精灵设备拆机及系统分析

我手头的这个天猫精灵型号为TG_X1,黑色天猫精灵AI助手,拆解没有多少难度,但需要注意连接线的问题。具体的拆机过程可以参见:http://www.geekifix.com/wp/archives/415.html注意一点就是,天猫精灵的主板的位置,位于音频按键的底部,也就是顶端,而拆解是从电源的位置从低到顶进行拆机;拆机后,看到主控板的信息如下:主控型号:联发科 MT8516MT8516支持四核...

LeeKwen的专栏 3万+

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 323

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【JVM】对象内存布局

HotSpot JVM 中,Java 对象内存布局由对象头(12字节,含Mark Word与压缩Klass Pointer)、实例数据及对齐填充构成,总大小为8字节倍数。对象头支持锁状态动态复用,指针压缩使引用仅4字节,但堆超32GB时失效。实例字段按类型优先级重排序以减少间隙,最小对象大小为16字节。通过JOL工具可精准验证布局,理解锁机制、GC、伪共享等底层行为。

2301_79732484的博客 139

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain&#39;s apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = &quot;R. B. Palm&quot;, title = &quot;Prediction as a candidate for learning deep hierarchical models of data&quot;, year = &quot;2012&quot;,}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath(&#39;DeepLearnToolbox&#39;));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand(&#39;state&#39;,0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W&#39;); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand(&#39;state&#39;,0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = &#39;sigm&#39;;%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.10, &#39;Too big error&#39;);```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand(&#39;state&#39;,0)sae = saesetup([784 100]);sae.ae{1}.activation_function = &#39;sigm&#39;;sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)&#39;)% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;;nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.16, &#39;Too big error&#39;);```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x&#39;,28,28,60000))/255;test_x = double(reshape(test_x&#39;,28,28,10000))/255;train_y = double(train_y&#39;);test_y = double(test_y&#39;);%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you&#39;ll get around 1.2% errorrand(&#39;state&#39;,0)cnn.layers = { struct(&#39;type&#39;, &#39;i&#39;) %input layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 6, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %sub sampling layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 12, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er&lt;0.12, &#39;Too big error&#39;);```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.08, &#39;Too big error&#39;);%% ex2 neural net with L2 weight decayrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.1, &#39;Too big error&#39;);%% ex3 neural net with dropoutrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.1, &#39;Too big error&#39;);%% ex4 neural net with sigmoid activation functionrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.1, &#39;Too big error&#39;);%% ex5 plotting functionalityrand(&#39;state&#39;,0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = &#39;softmax&#39;; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.1, &#39;Too big error&#39;);%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand(&#39;state&#39;,0)nn = nnsetup([784 20 10]); nn.output = &#39;softmax&#39;; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er &lt; 0.1, &#39;Too big error&#39;);```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free &quot;Bitdeli Badge&quot;)

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 189

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 111

LeetCode Hot 100 二叉树专题(二)3 题(Java)

本篇文章共 3 题:543. 二叉树的直径、102. 二叉树的层序遍历、108. 将有序数组转换为二叉搜索树。 每道题都统一按「① 题目分析 & 解题思路 → ② 常见解法(附代码)及讲解 → ③ 知识点总结」的顺序组织;代码使用 Java 编写,节点定义沿用 LeetCode 默认的 TreeNode。

2401_88612756的博客 351

Decorator

TaskDecorator 是异步场景下"跨线程传递上下文"的标准方案,主要用于传递 MDC、用户信息、数据源路由等上下文数据,核心价值是让异步任务的日志链路不中断、业务上下文不丢失。TaskDecorator 是 Spring 提供的一个钩子接口,用于在异步任务执行前对 Runnable 进行包装,常用于跨线程传递上下文。如果你在项目中遇到具体的异步上下文传递问题,可以把代码贴出来,我帮你给出最合适的 Decorator 方案 👍。✅ 核心能力:在线程切换时,把"父线程的上下文"搬到"子线程"

wlwork66的博客 101

数据库乐观锁深度解析:MySQL、PostgreSQL 实战 + Spring Boot 集成指南

Oracle 提供了ORA_ROWSCN伪列(System Change Number),记录最后修改行的 SCN,类似 PostgreSQL 的xmin。实践中通常仍使用 version 字段方案,逻辑与 MySQL 完全相同。-- Oracle:通过 ORA_ROWSCN 实现乐观锁-- 更新时比对 SCN// 自定义注解// AOP 切面@Aspect@Component@Slf4jtry {

yjc_1111的专栏 390

JDK 下载安装:装完 Java 提示「不是内部或外部命令」?环境变量没配

JDK 是写 Java 程序必须装的东西。它包含运行 Java 的虚拟机(JRE)和编译 Java 的编译器(javac)——装了它,能跑、.java文件能编译成可执行程序。免费,学习和小规模生产都不花钱。

科小怪的博客 607

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 369

java框架大全

java框架大全

qq_39326137的博客 180

SecureRandom做随机源,这个Java随机字符串工具类有3个方法

本文分享一款JDK17环境下的Java随机字符串工具类,内置3个开箱即用的方法,全部采用SecureRandom加密安全随机源,不存在弱随机、可预测、取模偏差等问题。工具类支持默认62字符集随机串、纯数字随机串、自定义字符集随机串三种生成模式,支持Unicode码点级别的字符集处理,兼容Emoji等增补字符,边界处理严格,异常信息明确,适合验证码、邀请码、盐值、随机口令、短链码等场景,源码可直接复制使用。

Java后端 | AI智能体应用实战派 | 电脑技能行家 291

Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

【代码】Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

m0_64476639的博客 313

Agent 状态栏在上下文中的具体位置

Agent状态栏以user角色消息形式追加至上下文末尾,而非修改system消息,避免破坏KVCache。此举仅为API技术实现,内容由框架自动生成,非真实用户输入,通过&lt;agent_status&gt;标签标识,确保模型获取最新状态且不影响缓存。该设计遵循“动态信息追加、静态信息固定”原则,实现高效、低延迟的状态传递。

lj1109053360的专栏 67
上一篇: sql小技巧:日期区间和格式化操作
下一篇: React基础学习
冷环渊
博客等级 码龄7年 1130粉丝 176原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冷环渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值