LockSupport详解

目录

  • LockSupport详解
    • 1、LockSupport简介
      • LockSupport 类的构造方法
      • LockSupport 类的属性
      • `Thread`类的`parkBlocker`属性
      • LockSupport 类的常用方法
      • 挂起线程的相关方法
      • 唤醒线程的相关方法
      • `unpark(Thread thread)`方法注意点
      • LockSupport使用示例
      • 判断`park`的条件建议使用`while`而不是`if`
      • 引出Guarded Suspension模式
      • 总结
    • 2、LockSupport原理分析
      • 总结下LockSupport类设计思路的关键点:
    • 3、`park\unpark` VS `wait\notify\sleep\await\singnal`

LockSupport详解

1、LockSupport简介

LockSupport 是 Java 并发包(java.util.concurrent)中的一个工具类,提供了基本的线程阻塞和唤醒机制。它主要用于实现锁和其他同步类,如AQS、 ReentrantLock、Semaphore、CountDownLatch 等。

LockSupport 提供的主要方法是 parkunpark,它们分别用于挂起和唤醒线程(LockSupport.park()会让线程进入WATING状态)。 AQS中对线程进行挂起和唤醒操作最终使用的就是LockSupport.park(xxx);LockSupport.unpark(xxx)。 AQS相关内容可以参考 我的上一篇博客 AQS详解。

LockSupport 类的构造方法

// 构造方法是私有的,意味着LockSupport类不能被实例化。
// 这个设计表明LockSupport类只是一个工具类,提供静态方法供外部调用。
private LockSupport() {}

LockSupport 类的属性

// 这个静态常量持有一个Unsafe类的实例。Unsafe类提供了一些底层操作的能力。
// 如直接内存访问、CAS操作等。由于其强大的功能和潜在的危险性,Unsafe类的使用受到严格限制,通常只在JDK内部使用。
private static final sun.misc.Unsafe UNSAFE;//parkBlockerOffset常量保存了Thread类中parkBlocker字段的内存偏移量。parkBlocker字段用于记录调用park方法时的阻塞对象。
private static final long parkBlockerOffset;
// 这些静态常量分别保存了Thread类中
// threadLocalRandomSeed
// threadLocalRandomProbe
// threadLocalRandomSecondarySeed字段的内存偏移量。
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;static {try {// 获取 Unsafe 实例UNSAFE = sun.misc.Unsafe.getUnsafe();// 获取 Thread 类的相关字段的偏移量Class<?> tk = Thread.class;// 获取 parkBlocker 字段的偏移量parkBlockerOffset = UNSAFE.objectFieldOffset(tk.getDeclaredField("parkBlocker"));// 获取 threadLocalRandomSeed 字段的偏移量SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));// 获取 threadLocalRandomProbe 字段的偏移量PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));// 获取 threadLocalRandomSecondarySeed 字段的偏移量SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));} catch (Exception ex) {throw new Error(ex);}
}

总结:
LockSupport类提供了线程阻塞和唤醒的基础设施,通过使用Unsafe类进行底层内存操作来实现。其核心是利用park和unpark方法来管理线程的状态,确保并发编程中的高效和安全。
通过获取Thread类中关键字段的偏移量,LockSupport类能够直接操作这些字段,实现对线程状态的控制。
关于sun.misc.Unsafe UNSAFE类,后续再另写一篇博客分析。

Thread类的parkBlocker属性

volatile Object parkBlocker;

Thread类的parkBlocker属性是一个volatile的Object类型变量,用于记录线程在调用LockSupport.park(Object blocker)时被阻塞的原因或对象。通过parkBlocker,我们可以在调试或分析时更容易地了解线程的阻塞原因。这种设计有助于提高并发编程的可调试性和可维护性。

比如有一个锁对象,线程在获取锁时被阻塞,可以通过parkBlocker记录这个锁对象,以便在调试或分析时知道线程因为什么原因被阻塞。

为啥这样设计呢?
因为在JDK1.5的时候 LockSupport 没设计Thread 类的parkBlocker来记录阻塞信息,这导致分析线程变得困难。所以1.6加入了这个特性。 此外虽然 synchronized 本身不支持像 parkBlocker 这样的灵活机制但是在 JVM 内部,synchronized 会记录阻塞线程的锁对象,这也有利于调试。

下面的park(Object blocker)方法就是这么用的。

LockSupport 类的常用方法

挂起线程的相关方法

①、park(Object blocker)方法:
挂起当前线程,并设置阻塞对象。阻塞对象通常用于调试或监视线程状态。挂起结束后清除阻塞对象。

public static void park(Object blocker) {// 获取当前线程Thread t = Thread.currentThread();// 设置阻塞对象setBlocker(t, blocker);// 挂起当前线程UNSAFE.park(false, 0L);// 注意: (这里因为上面的  UNSAFE.park(false, 0L) 会让线程挂起)  // 当线程被唤醒的时候必须要清除唤醒线程的blocker对象 或者下面直接跟了setBlocker(t, null); // 这就保证了当线程被唤醒之后能够确保清除唤醒线程的blocker对象// 挂起结束后清除阻塞对象 setBlocker(t, null);
}// 通过 UNSAFE.putObject 方法将阻塞对象 arg 设置到线程 t 中的 parkBlocker 字段中。
private static void setBlocker(Thread t, Object arg) {UNSAFE.putObject(t, parkBlockerOffset, arg);
}// park 方法是一个本地方法,具体实现依赖于底层操作系统
public native void park(boolean isAbsolute, long time);

②、parkNanos(Object blocker, long nanos)方法:
挂起当前线程指定的纳秒数,并设置阻塞对象。挂起结束后清除阻塞对象。

public static void parkNanos(Object blocker, long nanos) {if (nanos > 0) {// 获取当前线程Thread t = Thread.currentThread();// 设置阻塞对象setBlocker(t, blocker);// 挂起当前线程指定的纳秒数UNSAFE.park(false, nanos);// 挂起结束后清除阻塞对象setBlocker(t, null);}
}

③、parkUntil(Object blocker, long deadline)方法:
挂起当前线程直到指定的时间,并设置阻塞对象。挂起结束后清除阻塞对象。

public static void parkUntil(Object blocker, long deadline) {// 获取当前线程Thread t = Thread.currentThread();// 设置阻塞对象setBlocker(t, blocker);// 挂起当前线程直到指定的时间UNSAFE.park(true, deadline);// 挂起结束后清除阻塞对象setBlocker(t, null);
}

④、park()方法:
挂起当前线程,直到其他线程调用unpark或线程被中断。

public static void park() {// 挂起当前线程UNSAFE.park(false, 0L);
}

⑤、parkNanos(long nanos)方法:
挂起当前线程指定的纳秒数。

public static void parkNanos(long nanos) {if (nanos > 0) {// 挂起当前线程指定的纳秒数UNSAFE.park(false, nanos);}
}

⑥、parkUntil(long deadline)方法:
挂起当前线程直到指定的时间。

public static void parkUntil(long deadline) {// 挂起当前线程直到指定的时间UNSAFE.park(true, deadline);
}

唤醒线程的相关方法

unpark(Thread thread)方法:
unpark方法用于唤醒被park方法挂起的线程。

public static void unpark(Thread thread) {// 检查传入的线程是否为 nullif (thread != null) {// 使用 UNSAFE 类的 unpark 方法唤醒指定的线程UNSAFE.unpark(thread);}
}

unpark(Thread thread)方法注意点

这个方法上注释有一句话:
This operation is not guaranteed to have any effect at all if the given thread has not been started.
意思是:如果给定的线程尚未启动(也就是线程调用start方法之前的状态),则无法保证此操作有效。

比如对一个处于NEW状态的线程 调用unpark,再让线程start,当线程处于RUNNABLE状态后再调用park方法,那么这个线程可能会被挂起。也就是 unpark方法不一定生效。

这里先留个印象,下面第2节 分析 LockSupport原理 还会提到。

看下面的例子:

import java.util.concurrent.locks.LockSupport;public class TestA {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// ③、t1此时的状态是 RUNNABLE ,再 park t1线程LockSupport.park();  // 可能会阻塞  因为 unpark方法不保证 对未启动的线程生效System.out.println("123");}, "t1");// ①、先 unpark  NEW状态的 线程 t1LockSupport.unpark(t1);// ②、启动 t1线程t1.start();}
}

上面代码, t1线程尚未启动(也就是线程调用start方法之前的状态) LockSupport.unpark(t1) 未生效。

LockSupport使用示例

利用LockSupport,实现线程间通信,交替打印 牛 马 人

import java.util.concurrent.locks.LockSupport;public class TestA {// t1负责打印牛// t2负责打印马// t3负责打印人private static Thread t1, t2, t3;// 控制打印顺序的状态变量 0表示执行t1 唤醒t2// 控制打印顺序的状态变量 1表示执行t2 唤醒t3// 控制打印顺序的状态变量 2表示执行t3 唤醒t1private static volatile int state = 0;public static void main(String[] args) {t1 = new Thread(() -> {for (int i = 0; i < 3; ++i) {while (state != 0) {LockSupport.park(); // 挂起 t1 线程,直到被唤醒}System.out.print("牛 ");state = 1; // 设置为 1,表示下一个打印马LockSupport.unpark(t2); // 唤醒 t2}});t2 = new Thread(() -> {for (int i = 0; i < 3; ++i) {while (state != 1) {LockSupport.park(); // 挂起 t2 线程,直到被唤醒}System.out.print("马 ");state = 2; // 设置为 2,表示下一个打印人LockSupport.unpark(t3); // 唤醒 t3}});t3 = new Thread(() -> {for (int i = 0; i < 3; ++i) {while (state != 2) {LockSupport.park(); // 挂起 t3 线程,直到被唤醒}System.out.print("人 ");state = 0; // 设置为 0,表示下一个打印牛LockSupport.unpark(t1); // 唤醒 t1}});t1.start();t2.start();t3.start();}
}

运行结果:

牛 马 人 牛 马 人 牛 马 人 

判断park的条件建议使用while而不是if

LockSupport源码中注释也是这么建议的:

while (!canProceed()) { ... LockSupport.park(this); }

为什么这么用呢?
如果使用 if 条件,线程被唤醒后只检查一次条件。如果是条件不满足的唤醒,但线程已经继续执行,这会导致错误的行为。
而while 循环在每次被唤醒时都会重新检查条件。如果条件仍然不满足,线程会继续等待。这确保了线程在条件未满足时不会继续执行。

比如上面打印牛马人示例中:


new Thread(() -> {for (int i = 0; i < 3; ++i) {while (state != 2) {  // 这里的while 换成两次if也可以LockSupport.park(); // 挂起 t3 线程,直到被唤醒}System.out.print("人 ");state = 0; // 设置为 0,表示下一个打印牛LockSupport.unpark(t1); // 唤醒 t1}
});// 上代码可以用下面代码替换   因为条件比较简单 
new Thread(() -> {for (int i = 0; i < 3; ++i) {if (state != 2) {LockSupport.park(); // 挂起 t3 线程,直到被唤醒}if (state == 2) {System.out.print("人 ");state = 0; // 设置为 0,表示下一个打印牛LockSupport.unpark(t1); // 唤醒 t1}else {LockSupport.park();}}});

明显可以看出,使用while更加简洁明了。 实际上即使是很简单的state != 2 的条件用if看着也很臃肿了,如果条件比较复杂就更不推荐多个if检查条件了,会让代码变得臃肿且难以理解和维护。

引出Guarded Suspension模式

在《图解Java多线程设计模式》这本书中第82页就有介绍这个模式。下面引用一下原书的内容。

Guarded是被守护、被保卫、被保护的意思Suspension则是“暂停”的意思。如果执行现在的处理会造成问题,就让执行处理的线程进行等待–这就是 Guarded Suspension 模式。

当你正在家换衣服时,门铃突然响了,原来是邮递员来送邮件了。这时,因为正在换衣服出不去,所以只能先喊道“请稍等一下”,让邮递员在门口稍等一会儿。换好衣服后,才说着“让您久等了”并打开门。

这个例子很日式呀,让您久等了~ 因为这本书作者就是日本人 。 非常推荐大家看看这本书《图解Java多线程设计模式》 。

再拿上面打印牛马人的例子说明一下问题,其中利用 while来判断park条件 就是为了保证线程只打印该打印的东西,不该打印的时候就挂起。

总结

LockSupport类提供了多种挂起线程的方法,主要是通过Unsafe类的park方法实现。挂起线程的方法分为带阻塞对象的和不带阻塞对象的,带阻塞对象的方法在挂起线程时会记录阻塞的原因或对象,以便调试和监视。每种方法还支持不同的挂起时长,包括无限期挂起、挂起指定的纳秒数和挂起直到某个时间点。通过这些方法,LockSupport类为高级并发控制提供了基础设施。比如AQS以及依赖AQS为基础实现的锁或者同步器,最终都是通过LockSupport提供的parkunpark实现的线程挂起和唤醒。

2、LockSupport原理分析

先思考一个问题
看下面代码:

import java.util.concurrent.locks.LockSupport;public class TestA {public static void main(String[] args) throws InterruptedException {LockSupport.unpark(Thread.currentThread());LockSupport.park();System.out.println("LockSupport  先unpark 后park");TestA testA = new TestA();synchronized (testA) {testA.notify();testA.wait();System.out.println("synchronized  先notify 后wait");}}
}

运行结果:
在这里插入图片描述

根据代码执行的结果可以看出:
代码调用 LockSupport.unpark 来唤醒当前线程。即使当前线程没有被阻塞,这个操作也会起作用。
当前线程已经调用过一次 LockSupport.unpark 后,再调用 LockSupport.park()就不会被阻塞了,因为已经提前唤醒了一次。

synchronized 同步代码块中 先调用 notify没有任何作用,因为没有线程在 testA锁对象 上等待。
当前线程已经调用过一次 testA.notify(); 后,再调用 testA.wait(); 线程仍然会被阻塞。

总结:
LockSupport 提供了更灵活的阻塞和唤醒机制,能够在任何时候调用 unpark 而不必担心线程是否已经阻塞。

下面这段内容来自Java官方的JDK1.6中文版文档。
(如果英语掌握的很好,还是推荐自己去阅读源码上的英文注释比较好,虽然下面是官方的JDK1.6中文版文档,但是翻译的还是比较生硬~)

用来创建锁和其他同步类的基本线程阻塞原语。 此类以及每个使用它的线程与一个许可关联(从 Semaphore 类的意义上说)。如果该许可可用,并且可在进程中使用,则调用 park 将立即返     
回;否则可能 阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。(但与 Semaphore 不同的是,许可不能累积,并且最多只能有一个    
许可。) park 和 unpark 方法提供了阻塞和解除阻塞线程的有效方法,并且不会遇到导致过时方法 Thread.suspend 和 Thread.resume 因为以  
下目的变得不可用的问题:由于许可的存在,调用 park 的线程和另一个试图将其 unpark 的线程之间的竞争将保持活性。此外,如果调用者线  
程被中断,并且支持超时,则 park 将返回。park 方法还可以在其他任何时间“毫无理由”地返回,因此通常必须在重新检查返回条件的循环里  
调用此方法。从这个意义上说,park 是“忙碌等待”的一种优化,它不会浪费这么多的时间进行自旋,但是必须将它与 unpark 配对使用才更高  
效。   三种形式的 park 还各自支持一个 blocker 对象参数。此对象在线程受阻塞时被记录,以允许监视工具和诊断工具确定线程受阻塞的原因。  
(这样的工具可以使用方法 getBlocker(java.lang.Thread) 访问 blocker。)建议最好使用这些形式,而不是不带此参数的原始形式。  
在锁实现中提供的作为 blocker 的普通参数是 this。   这些方法被设计用来作为创建高级同步实用工具的工具,对于大多数并发控制应用程序而言,它们本身并不是很有用。park 方法仅设计用于以下  
形式的构造: while (!canProceed()) { ... LockSupport.park(this); }在这里,在调用 park 之前,canProceed 和其他任何动作都不会锁定  
或阻塞。因为每个线程只与一个许可关联,park 的任何中间使用都可能干扰其预期效果。 示例用法。 以下是一个先进先出 (first-in-first-out) 非重入锁类的框架。 class FIFOMutex {private final AtomicBoolean locked = new AtomicBoolean(false);private final Queue<Thread> waiters= new ConcurrentLinkedQueue<Thread>();public void lock() {boolean wasInterrupted = false;Thread current = Thread.currentThread();waiters.add(current);// Block while not first in queue or cannot acquire lockwhile (waiters.peek() != current ||!locked.compareAndSet(false, true)) {LockSupport.park(this);if (Thread.interrupted()) // ignore interrupts while waitingwasInterrupted = true;}waiters.remove();if (wasInterrupted)          // reassert interrupt status on exitcurrent.interrupt();}public void unlock() {locked.set(false);LockSupport.unpark(waiters.peek());}}

总结下LockSupport类设计思路的关键点:

  • ①、许可机制: LockSupport通过一个许可的概念来控制线程的阻塞和恢复。这个许可类似于Semaphore中的许可,但需要注意的是它不支持累积,即最多只有一个许可可用。这意味着即使多次调用unpark也只会保留一个许可。

  • ②、阻塞操作: LockSupport.park()方法会尝试获取许可。如果许可存在,线程会立即返回并继续执行。如果没有许可,线程将被阻塞,直到另一个线程调用LockSupport.unpark(Thread)方法来释放许可,或者线程接收到中断信号。

  • ③、非累积性: 与Semaphore不同,LockSupport的许可不能累积。这意味着即使一个线程被多次unpark,它也只能被唤醒一次,多余的unpark操作不会产生额外的效果。
    例如:

import java.util.concurrent.locks.LockSupport;public class TestA {public static void main(String[] args) {// unpark 主线程两次LockSupport.unpark(Thread.currentThread());LockSupport.unpark(Thread.currentThread());// park主线程两次LockSupport.park();System.out.println("park第一次");  // 正常打印LockSupport.park();   // 在这就会阻塞System.out.println("park第二次"); // 不打印}
}
  • ④线程关联: 每个线程都有一个与之关联的LockSupport许可。当一个线程调用unpark时,它必须指定要唤醒的线程。这允许了更细粒度的控制,因为可以精确地选择哪个线程应该被唤醒。

  • ⑤、中断处理: 当一个线程被阻塞时,它可以被中断。LockSupport.park()会检查中断状态,并在检测到中断时抛出InterruptedException。这使得LockSupport可以安全地与其他Java中断机制协同工作。

至于再底层的实现就是通过Unsafe类,以及操作系统提供的原语了。
例如,在 Linux 上,LockSupport 可能会利用 pthread 库中的 pthread_mutex_lock 和 pthread_cond_wait 等函数来实现这些功能。这里就不再细说了,水平有限~

3、park\unpark VS wait\notify\sleep\await\singnal

直接列个表格简单直观:

方法描述对比
park/unparkpark 阻塞当前线程,直到被 unpark 唤醒。unpark 唤醒指定线程。直接操作线程,不依赖锁或条件。
可以在非同步块中使用。
提供更细粒度的控制。
park 可以指定阻塞时间。
可以先 unparkpark
由于park不依赖于锁或者其他条件,所以如果一个线程持有锁,调用了park,那么该线程挂起后并不会释放锁
wait/notifywait 使当前线程等待,直到被 notifynotifyAll 唤醒。需要在synchronized同步代码块中使用。
wait 会释放持有的对象锁。
notify 唤醒单个等待线程,notifyAll 唤醒所有等待线程。
notifywait之前调用没有效果
sleep使当前线程休眠指定时间。不释放锁。
主要用于时间控制。
休眠时间结束后自动恢复。
await/signal用于 Condition 对象上的线程等待和唤醒。必须在锁条件下使用。
await 会释放锁并进入等待状态,直到被 signalsignalAll 唤醒。
用于更复杂的同步需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/47904.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法篇 滑动窗口 leetCode 水果成篮

水果成蓝 1.题目描述2.图形分析2.1原理解释2.2 怎么想出使用滑动窗口2.3 图形分析 3.代码演示 1.题目描述 2.图形分析 2.1原理解释 2.2 怎么想出使用滑动窗口 2.3 图形分析 3.代码演示

Android 10.0 Launcher3拖拽图标进入hotseat自适应布局功能实现一

1.前言 在10.0的系统rom定制化开发中&#xff0c;在对于launcher3的一些开发定制中&#xff0c;在对hotseat的一些开发中&#xff0c;需要实现动态hotseat居中 的功能&#xff0c;就是在拖拽图标进入和拖出hotseat&#xff0c;都可以保持hotseat居中的功能&#xff0c;接下来分…

【Linux】基础I/O——理解ext2文件系统

我们到现在为止讲的都是打开的文件。现在我们讲讲没有打开的文件 如果一个文件没有被打开&#xff0c;那它就是在磁盘中被存储的&#xff0c;我们就要关心路径问题&#xff0c;存储问题&#xff0c;文件获取问题&#xff0c;那么操作系统是怎么处理这些问题的&#xff1f;不急…

配置SMTP服务器的要点是什么?有哪些限制?

配置SMTP服务器安全性如何保障&#xff1f;如何高效配置服务器&#xff1f; SMTP作为电子邮件发送的核心协议&#xff0c;其配置对于确保邮件的成功传递和安全至关重要。AokSend将详细介绍配置SMTP服务器的关键要点&#xff0c;帮助读者建立一个高效、安全的邮件发送系统。 配…

使用 Flask 3 搭建问答平台(三):注册页面模板渲染

前言 前端文件下载 链接https://pan.baidu.com/s/1Ju5hhhhy5pcUMM7VS3S5YA?pwd6666%C2%A0 知识点 1. 在路由中渲染前端页面 2. 使用 JinJa 2 模板实现前端代码复用 一、auth.py from flask import render_templatebp.route(/register, methods[GET]) def register():re…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署Hallo :针对肖像图像动画的分层音频驱动视觉合成

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本文目标&#xff1a;在Ubuntu系统上部署Hallo&#x…

Python面试宝典第15题:岛屿数量

题目 在二维网格地图上&#xff0c;1 表示陆地&#xff0c;0 表示水域。如果相邻的陆地可以水平或垂直连接&#xff0c;则它们属于同一块岛屿。请进行编码&#xff0c;统计地图上的岛屿数量。比如&#xff1a;下面的二维网格地图&#xff0c;其岛屿数量为3。 基础知识 解决这类…

国产化低功耗HDMI转VGA方案,大量出货产品,广泛应用在显示器以及广告机产品

芯片描述&#xff1a; 兼具高性能和低成本效益的优点&#xff0c;是一款可以将高清视频 HDMI1.4 数字信号转换成 VGA 模拟信号输出的芯片。不需要提供外部电源&#xff0c;ICNM7301 就可以在正常模式下使用&#xff1b;ICNM7301 广 泛适用于各种市场系统和显示应用体系&#x…

Yum包下载

1. 起因 内网有一台服务器需要升级php版本,维护的同学又不想二进制安装.服务器只有一个光盘的yum仓库 2. 解决方法 解决思路如下: 外网找一台机器配置php8.3.8的仓库外网服务器下载软件集并打包内网服务器上传并解压实现升级 2.1 下载php8.3.8仓库 配置php仓库 rootcent…

【视频讲解】神经网络、Lasso回归、线性回归、随机森林、ARIMA股票价格时间序列预测|附代码数据

全文链接&#xff1a;https://tecdat.cn/?p37019 分析师&#xff1a;Haopeng Li 随着我国股票市场规模的不断扩大、制度的不断完善&#xff0c;它在金融市场中也成为了越来越不可或缺的一部分。 【视频讲解】神经网络、Lasso回归、线性回归、随机森林、ARIMA股票价格时间序列…

新时代多目标优化【数学建模】领域的极致探索——数学规划模型

目录 例1 1.问题重述 2.基本模型 变量定义&#xff1a; 目标函数&#xff1a; 约束条件&#xff1a; 3.模型分析与假设 4.模型求解 5.LINGO代码实现 6.结果解释 ​编辑 7.敏感性分析 8.结果解释 例2 奶制品的销售计划 1.问题重述 ​编辑 2.基本模型 3.模…

【网络】Socket编程

文章目录 正确理解端口号理解源IP地址和目的IP地址认识端口号端口号和进程ID 理解Socket网络字节序socket编程接口创建socket套接字bind绑定套接字listen建立监听accept接受连接connect建立连接sendto发送数据接收数据close关闭套接字 sockaddr结构体 正确理解端口号 理解源IP…

使用崖山YMP 迁移 Oracle/MySQL 至YashanDB 23.2 验证测试

前言 首届YashanDB「迁移体验官」开放后&#xff0c;陆续收到「体验官」们的投稿&#xff0c;小崖在此把优秀的投稿文章分享给大家~今天分享的用户文章是《使用崖山YMP 迁移 Oracle/MySQL 至YashanDB 23.2 验证测试》&#xff08;作者&#xff1a;尚雷&#xff09;&#xff0c…

PHP宠物店萌宠小程序系统源码

&#x1f43e;萌宠生活新方式&#x1f43e; &#x1f3e1;【一键直达萌宠世界】 你是否也梦想着拥有一家随时能“云撸猫”、“云吸狗”的神奇小店&#xff1f;现在&#xff0c;“宠物店萌宠小程序”就是你的秘密花园&#xff01;&#x1f31f;只需轻轻一点&#xff0c;就能瞬…

什么是股指期货交割?股指期货交割的例子

股指期货交割是指在股指期货合约到期时&#xff0c;投资者需要按照合约规定完成的结算过程。与一般的商品期货、国债期货或外汇期货不同&#xff0c;股指期货采用的是现金交割方式。 股指期货交割的方式 【现金交割】股指期货的交割不需要实际交割一篮子股票指数成分股。相反…

(社恐福音)用python写一个定时弹窗功能

背景 背景是换了一个工作&#xff0c;需要点外卖了 写代码太认真的时候又经常忘记 这时候就需要一个闹钟 手机闹钟声音太大 会影响他人 所以用python 写一个弹窗功能&#xff0c;只影响自己 效果图 原理 管理列表和定时功能通过windows自带的计划完成 python程序不用占用后台…

7月18日学习打卡,数据结构堆

hello大家好呀&#xff0c;本博客目的在于记录暑假学习打卡&#xff0c;后续会整理成一个专栏&#xff0c;主要打算在暑假学习完数据结构&#xff0c;因此会发一些相关的数据结构实现的博客和一些刷的题&#xff0c;个人学习使用&#xff0c;也希望大家多多支持&#xff0c;有不…

ARM架构(二)—— arm v7/v8/v9寄存器介绍

1、ARM v7寄存器 1.1 通用寄存器 V7 V8开始 FIQ个IRQ优先级一样&#xff0c; 通用寄存器&#xff1a;31个 1.2 程序状态寄存器 CPSR是程序状态毒存器&#xff0c;保存条件标志位&#xff0c;中断禁止位&#xff0c;当前处理器模式等控制和状态位。每种异常模式下还存在SPSR&…

《系统架构设计师教程(第2版)》第12章-信息系统架构设计理论与实践-02-信息系统架构

文章目录 1. 概述1.1 信息系统架构&#xff08;ISA&#xff09;1.2 架构风格 2. 信息系统架构分类2.1 信息系统物理结构2.1.1 集中式结构2.1.2 分布式结构 2.2 信息系统的逻辑结构1&#xff09;横向综合2&#xff09;纵向综合3&#xff09;纵横综合 3. 信息系统架构的一般原理4…

Android使用ANativeWindow更新surfaceView内容最简Demo

SurfaceView简介 SurfaceView对比View的区别 安卓的普通VIew,都依赖于当前Activity的Window的surface&#xff0c;这个surface用于承载view树从底到顶绘制出来的所有内容&#xff0c;因此任何一个view需要更新时&#xff0c;都需要把所有view中底到顶进行更新&#xff0c;即使使…