【高级网络程序设计】Block1总结

        这一个Block分为四个部分,第一部分是Introduction to Threads and Concurrency ,第二部分是Interruptting and Terminating a Thread,第三部分是Keep Threads safety:the volatile variable and locks,第四部分是Beyond simple locks:the monitor。

      第一部分:Introduction to Threads and Concurrency

        在这一部分中,首先介绍了Processes and Threads,然后讲了如何create a thread,接着说明了一些关于thread的方法,用来control your thread and coordinate your thread.

        1.  Processes and Threads

        关于Processes and Threads,我们首先用一张relative drawing直观的看出它们之间的关系,然后以JVM is a process来引出process与上图呼应,接着介绍了thread和process的区别,最后讲了在concurrent programming里面的四个重要概念。

        ① relative drawing

        ② JVM is a process

        与上图相对应,我们可以知道:a java application runs by default in a process; work with several threads to achieve psedo parallel process / asychronous behaviour.

        ③ Processes and Threads

        从四个方面来介绍进程和线程,运行独立,访问,资源分配

Processes Threads
run independently from othersa lightweight process, has its own call stack
cannot access data in other processescan access shares data of threads in the same process
the resources are allocated via the operating systemhas its own memory cache, reads shared data, store it and re-read the data

     ④ The key concepts in concurrent programming

        在并行编程中,有四个关键的概念:原子性,可见性,运行顺序和关键代码。

        原子性(atomicty)指的是不能再进行分割的代码,即:when it cannot be interrupted。一旦开始运行就结束了,即:once it starts it always complete。最典型的例子就是a++的非原子性,除此之外,X = new Integer(x)(create-assign the reference)、x = y(将y的值赋值给x read-write)、x.equals(y);(比较x和y的值是否相等)均为非原子操作,他们都是多个步骤。常见的原子操作有x=3这种简单的基本数据类型的赋值。【关于原子性,在后面的第三部分volatile variable会提到,原子性也是可以使用volatile的条件】

        可见性(visibility)指的是再此线程运行的时候必须看到其他线程的运行,即:when a thread must watch the actions of another thread。典型的例子是线程的终止,数值的设定。

        运行顺序(the order of execution),关于运行顺序,正常程序是run in the same order everytime,并行程序是the order of execution is never guaranteed。对于这个概念,可以出相关考题,给出一段多个线程的代码,判断输出,这时候运行顺序是不确定的,输出也是不确定的。

        关键代码(critical code)指的是只能由一个线程执行一次的代码部分,即:a part of code that only can be executed by a single thread at one time。【关于关键代码,在后面第三部分volatile variable中会再次出现,不同的是提出的是关键部分critical section】

        2. create a thread

        ① The thread class

        提到线程类(thread class),它的作用是executing your stuff in a thread,那stuff 在哪儿呢,your stuff is encapsulated in a run() method

       关于创建线程,我们有两种方式——使用implements Runnable和使用extends Thread,具体来说是:pass your class into a new Thread object,extend the Thread class

        Thread我们很熟悉了,是线程类,那Runnable是什么呢——Runnable is an interface that requires you to implement a run() method.

         因此,对于一个线程类,我们首先要implement/extend,然后写两个函数,一个是public void run(),一个是public static void main(String[] args)。

        ② Implements Runnable / extends Thread

        Implement Runnable 和 extend Thread除了在类名后面的内容上有所不同之外,在线程的创建上也有所不同。下面将解释一下:

public Order implements Runnablepublic Order extends Thread

Order order  = new Order();

Thread t1 = new Thread(order);

Order t1  = new Order();

         关于两种方式,更好的是Implements Runnable,因为Implements Runnable allows a subclass of Thread to be used if required,然而extends Thread——no other classes can be inherited by MyThread.

        3. control your thread

        ① 控制线程-sleep(), yield()

       关于控制线程,包括make your thread sleep interrupt your thread's sleep。完成这两个任务,我们需要去 make the code to execute at the appropriate time manage resource,一是时间二是资源。

        这里我们将介绍三种方法——sleep(), yield(), interrupt()以及interrupt的相关运作机制(InterrupedException,InterruptFlag,使用Interupt的反应和使用Interupt的情况)

        sleep():static method。它的作用是make the current thread sleep。使用之后它的结果是the thread will pause and will free up CPU for other threads

        yield():static method。它的结果是the executing thread is suspended and the CPU is given to other runnable thread。换一种说法是the executing thread is returned to the ready queue of the processor and wait for its next turn

        ② interrupt()

        在程序运行中会发生线程的blocka thread if prevented from doing anything),原因可能是waiting for a monitor lock 或者时suspended by call to wait()[become runnable on a notify/ notifyAll]。【Block发生的原因】

       那么当我们遇到block的时候如何处理呢——interrupt()!【使用Interupt的情况】

        interrupt():non-static method。当使用它的时候需要用t1.interrupt()/Thread.current Thread().interrupt(),那么在使用之后会发生什么呢?【interrupt()】

        分为两种情况,第一种是当线程sleep的时候,第二种是线程没有sleep的时候。sleep时,进行interrupt,首先会将interrupt flag中断标记设为true,然后会throw InterruptedException,同时会clear the interrupt flag清除中断标记;没有sleep时,进行interrupt,只会将interrupt flag中断标记设为true。【使用Interupt的反应】

        什么是InterruptedException,什么是interrupt flag中断标记呢?

        InterruptedException,会在the thread is interrupted while sleeping的时候发生,作用是enable you to deal with interrupt elegantly. 在try...catch(InterruptedException e){e.printStackTrace()}语句中进行使用。【InterrupedException】

        interrupt flag,存在于every Thread object,为了标记中断会change this flag to true。如果值为true时,需要finish the method immediatly;如果值为false,continual as normal。当然这也是建立在是否为sleep state的基础上。interrupt flag

        4. coordinate your thread

        对于协调线程,我们需要用到join()方法:non-static method。它的作用时pause util the other thread has terminated(use on any thread to wait for it to die),跟sleep一样,需要用到InterruptException

        第二部分:Interrupting and Terminating threads

        1. Interrupting threads

       关于中断线程,我们分为三个部分进行讲解——中断的概念,中断机制和判断线程是否中断的方法。

        ① Interrupt

        Interrupt(中断)an indication to a thread that it should stop what it is doing and do something else.(not stop the thread)。那我们如何使其停止呢?——The programmer decides how a thread responds to a thread。也就是说,首先需要进行中断(A thread sends an Interrupt by invoking interrupt() on the Thread object to be interrupted),然后再根据programmer指示去做,这个过程我们需要Interrupt mechenism的支持。【interrupt的概念、使用】

        ② Interrupt mechanism

        Interrupt mechanism的实现离不开中断标记(Interrupt flag/interrupt status)。对于中断标记的设置,我们需要用到Interrupt()函数,对于中断标记的检查,我们可以用两个方法——interrupted()和isInterrupted()

        ③ Interrupt methods

       下面我们将从类型、作用和特点三个方面来讲述两者的区别

Thread.interrupted()

t1. isInterrupted()
staticnon-static
check the current threadcheck the Thread object that it is called on
clear the status of the current thread/

        2. Terminating threads

        上一章节,我们提到了Exception,关于为什么使用Exception,我们有两种解释——deal with an unusual situation, elegantly finish whatever we are doing.但是终止结束线程,首先要保证线程是alive的,然后再进行终止方法的选择。

        ① IsAlive()

        线程alive指的是when it has been started and is not dead yet。判断线程alive需要用到isAlive()方法,thread is alive——return true。

        ② 3 ways to terminate

        关于终止线程,我们有三种方式,分别是——finish the thread naturally, deamon threads 和interrupt the thread。

        finish the thread naturally有两种情况——doing nothing(start-let it finish)和use a shared Boolean(periodically check to see if pleaseFinish is set to true, true——finish thread)。我们重点注意的是第二种使用Boolean的方式,示例代码如下:

public volatile boolean pleaseFinish = false;

public void finishThreadPlease(){

        pleaseFinish=true;

我们需要注意的是,这个方法使用的有效是存在条件的,即thread需要alive——runnable。non-runnable的时候有以下几种情况——sleep(), wait(), blocking on IO。如果我们想要迅速得到这个线程,可以使用interrupt方法。

        daemon threads也存在两种情况,但是与上述不同的是,它是通过方法setDeamon来设置的——setDeamon(true)和setDeamon(flase)。setDeamon(true)是the thread terminates when the parent thread terminates,setDeamon(false)是the thread continues to run after the parent thread has finished.需要注意的是,需要在线程开始之前进行设置,示例代码如下:

public void run(){

        WorkerThread t1 = new WorkerThread();

        t1.setDeamon(true);

        t1.start();        

}

        第三种方式也是我们熟悉的Interrupt,在此前面的内容不再赘述,就interrupt之后的反应再进行详细讨论。首先,我们需要知道interrupt是an indication并非强制停止,因此it will not automatically stop a thread unless you programme to do so。然后,在线程中断之后,我们需要做出反应,这时需要用到InterruptedException来抛出异常。但是呢,这个异常的抛出只是在sleep()/wait()方法作用之后才会发生,接下来我们就需要用到isInterrupted()和interruted()方法来routinely check for interrupts at strategic points where you can safely stop and cleanup.最后呢,我们再来讨论一下exception的情况,exception被抛出时说明线程是处在sleep()/wait()时,我们知道在exception被抛出之前的时候,sleep会将interrupt status清除——clear the interrupt status。为了重新保存中断状态,我们需要进行在exception的catch语句中重新进行一次中断。示例代码如下:

//InterruptedExcption抛出异常

public void run(){

       //...

        try{

               Thead.sleep(5000);

         }catch(InterruptedException e){

                e.printStackTrace();

        }

}

//restore the interrupt status

public void run(){

        //..

        try{

                Thread.sleep(5000);

        }catch(InterruptedException e){

                e.printStackTrace();

                Thread.currentThread().interrupt();

        }

}

        第三部分:Keep Threads safety:the volatile variable and locks

             当不同的线程访问同一个变量或者是同一个方法的时候,会出现冲突,为了解决变量访问的冲突,我们提出了volatile variable,为了解决方法访问的冲突,我们提出了lock。

        1. Keeping your code safe the volatile keyword 

        ① Thread safe code 

        关于线程安全有两个要点,首先是多线程操作共享数据,然后是不同时进行操作,即manipulate shared data structure in a manner that guarantees safe execution by multiple threads at the same time

        ② volatile

        volatile关键字很重要,没有了volatile关键字,代码就不能正常工作。那volatile有什么用呢?volatile是用来表示变量值会被不同线程修改,即indicate that  a variable's value will be modified by different threads,因此就可以实现变量只会被单线程访问,即access to the variable is only given to one thread at a time。但是并不是所有的变量都可以被volatile进行修饰,当加载、读取、写入等工作在变量上都是atomic的时候,才可以使用。【volatile的概念和特点】

        那我们什么时候不能够或者不需要使用volatile呢?有三种情况,变量为final,变量只会被单一线程访问 by single threadcomplex operation。示例代码如下:【volatile不需要使用的时候】

public class StoppableTask extends Thread{

        private volatile boolean pleaseStop;

        public void run(){

                while(!pleaseStop){// }

        }

        public void tellMeToStop(){

                pleaseStop = true;

        }

}

        ③ Common traps with volatile常见陷阱     

        关于volatile,我们知道使用条件具有原子性,访问具有单一性,因此在使用条件和访问上会有以下常见陷阱:

        如果将数组arr[]声明为volatile,数组的reference(arr)是volatile的,但是individual field accesses(arr[0],arr[1]...)不是thread-safe

        unary operation(++, --) 不是atomic,不是thread-safe>

        在数据访问过程中,我们需要进行变量缓存。但是如果变量被修改了,其他线程的缓存就会过时。因此,变量的值不会被本地缓存,所有的读取和写入都之间到main memory中。即 The value of variables will never be cached thread-locally, all reads and writes will go straight to "main memory"。

        2. Beyond volatile: using locks

        除了变量需要同一时刻被单一线程访问以外,还有代码块也需要如此。但是volatile只能对atomic operation进行操作(本章节只涉及变量——基本变量类型),不能对complex operation进行操作,所以我们需要引入lock。下面将介绍critical section和lock的相关问题,其中对于lock的介绍,我们分为

        ① Critical sections

          我们之前在第一部分提到过critical code的概念,即a part of code that can be executed by a single thread at a time。critical section将引入data和多个分别的并发的线程来进行另一种类似的表述,即 the code segments within a program that access the same data from within seperate, concurrent threads. 这部分critical section需要用synchronized关键词进行修饰,也称作synchronized section。对于synchronized section,只能在给定时刻被单一线程访问,即can only be accessed by a single thread at any given time.【critical section的概念,关键词和特点】

        ② Locks

        关于锁的使用,锁是作用在一个特定的代码部分上面的,即 A lock applies to a particular section of code。当我们使用锁之后,If the code is locked, no other thread can execute it. If the code is unlocked, any thread can take the lock and execute it。【锁的使用和效果】

        关于锁的分类,可以分为内部锁Intrinsic locks 和外部锁Extrinsic locksIntrinsic locks 是 每个对象都可以通过使用sychronized关键词来起到锁的作用。every object can function as a lock that is triggered using the keyword synchronized.

        其中,我们需要注意到锁的线程唯一性

        只有一个线程可以在同一时刻执行这个方法,当一个线程拥有lock时,其他线程不能再获取lock了,必须等到其释放。Only one thread can execute the method at the same time. When a thread has a lock, no other thread can acquire it. It must wait for the first thread to release the lock.

        如果两个方法被sychronized关键词修饰,只有一个可以执行,因为相同锁会被一个对象中的所有方法使用。Two methods with the synchronized keyword, only one method of the two will be executed at the same time. Because the same lock is used for all methods in an object.

        ③ Intrinsic lock 内部锁

         内部锁是每个对象通过使用sychronized关键词来起到锁的作用,那么我们如何来利用到sychronized关键词呢?我们有两种使用方式

        Synchronized methods

protected synchronized int getNextAvailableItem() {
    … return items;

protected int getNextAvailableItem() { 
    synchronized (this){
    … return items; 
    }
}

        如果方法为静态的话,那么使用的锁是Class对象——If the method is static, the used lock is the Class object

        Explicit use of the intrinsic lock(Synchronized statements)

public class Example {
    private int value = 0;
    public int getNextValue() {
        synchronized (this) {
            return value++;
        }
    }
}

        与synchronized methods不同, synchronized statements必须指定提供内部锁的对象——must specify the object that provides the intrinsic lock。通常情况下,关键部分是方法,也可以标记更小段为sychronized——critical sections in Java programs are methods. You can mark smaller code segments as synchronized. 对于大多数的java程序来说,最好再方法水平上使用synchronized。


        ④ Scope of a lock 锁的范围/作用域

        以上,我们讨论了lock的使用和效果——作用域特定的代码块,locked之后无法被执行;然后讨论了lock的分类,重点介绍了intrinsic lock——定义、两种不同的使用方式以及作用水平。现在我们将讨论锁的范围。

        lock的范围是在锁使用和释放之间的时间——The time between when the lock is taken and when the lock is released。那这个时间段是由什么来决定的呢?它是由代码段所决定的——Lock scopes can be determined by segments of code (a method or just a part of code)。再次注意,所作用的对象是对象不是方法——locks apply to objects not methods。并且,锁不是方法而是保护方法的东西—— A lock isn’t a method, it’s something that protects a method。

        ⑤ Full/Partial synchronization 完全同步和部分同步

        完全同步指的是每个方法都同步的类(无公共实例变量)保证了局部顺序行为—— A class in which every method is synchronised (that has no public instance variables) guarantees locally sequential behaviour. 因此,他们在一个时刻制作一件事情——They only do one thing at a time:ready (idle - not having the lock) / active (processing a method) / waiting (for a reply)

        ⑥ 其他

        讨论完完全同步,我们还有部分同步未讨论,现在我们要从方法的调用、方法的执行、方法的本身来进行详细的讨论:

        方法的调用:synchronized方法可调用unsynchronized方法——只有synchronized方法会获取lock,unsynchronized方法不会获取lock,所以可在synchronized方法中调用unsynchronized方法。synchronized方法可以调用另一个synchronized方法——同一object的synchronized方法share the same lock,已经持有lock的线程可继续执行其他synchronized方法,不需重新获取lock。

        方法的执行:标记为synchronized的方法或代码块将完整地执行——除非被wait方法explicitly suspended,否则在synchronized块完成之前,其他线程将被blocked。未被标记为synchronized的方法可以立即执行——即使对象的另一个方法正在执行,甚至是一个同步方法。对于非同步方法,没有要获取的锁,所以它们可以并行执行。

        方法的对象:每个object只有一个lock——每个object都有一个关联的lock,用于实现synchronization。对于static方法,使用的是该class对象的lock。

// 一个non-static方法可以使用代码块锁定静态数据—— 使用类对象作为同步锁
        synchronized (getClass()) {...}


// 只能在对象上进行同步操作,而不能在基本类型变量上进行同步。

synchronized (temp) { temp = 10; }


        方法的实现:lock是一个counter——每当一个thread进入一个synchronized方法或块时,就会增加。如果counter不为0,表示有thread已经持有该lock,当前thread将被阻塞,直到计数器为0。当线程退出synchronized方法或块时,counter会递减。

        方法的继承:synchronized关键字不会自动继承到子类方法中。当重写一个方法时,如果父类的方法被synchronized修饰,子类中覆盖该方法时需显式地加上synchronized,否则子类方法将不会同步

Synchronizedvolatile
method or scope declared synchronized primitive variable declared volatile
A synchronized method can protect more complex code

Access to a volatile variable never has the potential to block 

– Volatile only protects atomic operations

– not suitable for complex operations, a ++

        第四部分:Beyond simple locks:the monitor

        1. Monitor

        在保证线程安全中,我们引入了volatile,作用于变量,保证变量访问的安全性,然后引入了锁,作用于代码块,保证方法(代码块)使用的安全性。但是我们只是简单的让方法与方法之间互斥,并没有使其能够达到合作。因此提出了monitor

        ① From intrinsic locks to monitors

        关于intrinsic lock和monitor,两者之间在使用和作用方面不同。

intrinsic lockmonitor
support Mutual Exclusion through the use of the keyword synchronized  support Cooperation through the wait() & notify() methods
Only allow one thread to execute a part of code at a given time    

Enable threads to work together

(Wait and Notify monitors or Signal and Continue monitors)

        对于内部锁,实现是通过在方法/代码段上使用synchronized关键词,并通过对象调用方法或者在synchronized statement上指定作用对象来实现的。那monitor是如何实现的呢?Every object can be a monitor 。我们还需要三个部分来实现monitor

synchronizedcreates the lock to protect the critical section of the code
wait() on an objectpauses a thread and puts it in a wait set (the set of threads waiting for the lock to become free)
notify() on that objectreawakens a thread from the wait set

        对于Monitor来说,只是 another lock?在原有的基础上,Using the synchronized keyword ensure mutual exclusion—— Only one thread can execute the method at a given time,还添加了功能, allows cooperation between threads—— Allows threads to pause their execution and notify other threads of events.


② Entry set, wait set, owners


        对于这张图,我们可以看出分为左中右三个部分,左边是entry set(enter:进入lock region的线程——需要被放入相关monitor的entry set),中间是lock region/the owner(acquire:执行lock region的线程——没有其他的线程在entry set中等待,当前线程成功地获取了锁),右边是wait set( release and exit : the thread finishes executing the lock region)
          wait set是我们的重点,进入和出去两个途径进行讲述——release和acquire。如何release(进入wait set)呢?A thread that currently owns the lock can suspend itself inside the lock by executing a wait() command,执行wait()后——releases the lock and enters a wait set, the thread will stay suspended in the wait set until another thread executes a notify() command inside the lock。为了从wait set中出去,我们需要用到notify()——When a thread executes a notify, it continues to own the lock until it releases the lock of its own accord, either by executing a wait or by completing the lock region; After the notifying thread has released the lock, the waiting thread will be resurrected and will reacquire the lock.

        Notify 说明哪一组可以竞争—— wait or entry set
        ① If the (former) lock owner did not execute a notify before it released the lock:then only the threads in the entry set will compete to acquire the lock.
        If the (former) lock owner did execute a notify:then the entry set threads will have to compete with one or more threads from the wait set. 
        ② thread wins the competition
        from the entry set , it becomes the new owner of the lock.
        from the wait set wins the competition, it exits the wait set and reacquires the lock.

        A thread can only execute a wait command if it currently owns the lock – i.e., it is currently inside the synchronized block. 

        Notify有两种—— “notify”和“notify all”
        ① notify(): selects one thread arbitrarily from the wait set and marks it for eventual resurrection

        ② notifyAll():marks all threads currently in the wait set for eventual resurrection

        2. Deadlocks

        ① 定义:Two or more threads waiting for two or more locks to be freed, and the circumstances in the program is such that the locks will never be freed

        ② solutions
        • Prevention:Design code so deadlock is impossible

        – Avoid mutual exclusion

        – Allow pre-emption

        – Don’t allow a thread to hold multiple locks

        •  Avoidance:Steer around deadlock with smart scheduling

        • Detection and Recovery:Check for deadlock periodically; Recover by killing threads and restarting Deadlock solutions

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

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

相关文章

【算法系列篇】递归、搜索和回溯(四)

文章目录 前言什么是决策树1. 全排列1.1 题目要求1.2 做题思路1.3 代码实现 2. 子集2.1 题目要求2.2 做题思路2.3 代码实现 3. 找出所有子集的异或总和再求和3.1 题目要求3.2 做题思路3.3 代码实现 4. 全排列II4.1 题目要求4.2 做题思路4.3 代码实现 前言 前面我们通过几个题目…

提升研究效率,尽在EndNote 21 forMac/win!

在科研领域,文献管理是一项至关重要的任务。研究人员需要快速而准确地收集、整理和引用大量的文献资料,以支持他们的研究工作。而EndNote 21作为一款功能强大的文献管理软件,能够帮助研究人员高效地管理文献资源,提升研究工作的效…

【Linux基础】1. Linux 启动过程

文章目录 【 1. 内核的引导 】【 2. 运行init 】 运行级别 【 3. 系统初始化 】【 4. 建立终端 】【 5. 用户登录系统 】【 6. 图形模式与文字模式的切换方式 】【 7. Linux关机 】 Linux系统的启动过程分为 5个阶段: (1)内核的引导。 &#…

Java中线程状态的描述

多线程-基础方法的认识 截止目前线程的复习 Thread 类 创建Thread类的方法 继承Thread类,重写run方法实现Runnable接口,重写run方法使用匿名内部类继承Thread类,重写run方法使用匿名内部类实现Runnable接口,重写run方法使用Lambda表达式 run方法中的所有的代码是当前线程对…

第二百一十六回 分享一种更新页面数据的方法

文章目录 1. 概念介绍2. 实现方法2.1 实现思路2.2 实现方法3. 示例代码4. 内容总结我们在上一章回中介绍了"如何创建单例模式"相关的内容,本章回中将 分享一种更新页面数据的方法.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章回中介绍一种更新页…

测站坐标系统 -- 东北天(ENU)坐标系、站心坐标系

目录 一、测站坐标系的定义 二、测站坐标系与地心地固坐标系的转换 2.1地心地固坐标系转到测站坐标系 2.2测站坐标系转到地心地固坐标系 三、方位角和高度角的计算 一、测站坐标系的定义 测站坐标系统以观测站( 或地面上某一个观测点 ) 为中心建立坐标系统,将这…

SQL基础:记录的基本操作

在上一节中,我们进行了表的新建,这一节我们讲一下记录的增加、修改、删除、查询。 增加 增加即使用insert语句, INSERT INTO users (user_id, username, password, email) VALUES (2, jane_smith, pass456, janeexample.com);查看插入的数…

代码随想录第三十六天(一刷C语言)|背包问题理论基础分割等和子集

创作目的:为了方便自己后续复习重点,以及养成写博客的习惯。 一、背包问题 题目:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装…

Docker的安装及使用

目录 安装Docker 安装yum工具 更新本地镜像源 安装docker 启动docker 关闭防火墙 docker启动命令 配置镜像加速 docker的使用 拉取nginx 查看本地镜像 把镜像文件nginx导出成tar文件 查看是否导出成功 ​编辑 删除本地镜像nginx:latest 导入镜像文件nginx 拉取…

Java项目-瑞吉外卖项目优化Day1

创建新仓库 push项目 新建分支v1.0做优化 导入Redis相关配置 导入坐标。 实现配置类,重写序列化器,也可以直接用StringRedisTemplate。 application.xml配置: 实现缓存短信验证码 将手机号与验证码存进redis。 从redis中获取验证码&…

微信小程序长按图片识别二维码

设置show-menu-by-longpress"true"即可&#xff0c;长按图片后会弹出一个菜单&#xff0c;若图片中包含二维码或小程序码&#xff0c;菜单中会有响应入口 <image src"图片地址" show-menu-by-longpress"true"></image>官方说明

大语言模型(LLM)与 Jupyter 连接起来了!

现在&#xff0c;大语言模型&#xff08;LLM&#xff09;与 Jupyter 连接起来了&#xff01; 这主要归功于一个名叫 Jupyter AI 的项目&#xff0c;它是官方支持的 Project Jupyter 子项目。目前该项目已经完全开源&#xff0c;其连接的模型主要来自 AI21、Anthropic、AWS、Co…

专栏十六:bulk以及单细胞空转中的progeny通路分析

progeny本身有自己的R包,可以提取通路基因集信息,团队把他嵌入另一个R包decoupleR中完成富集分析。decoupleR自己有详细的针对bulk和scRNAseq的教程 简单安装一下 devtools::install_github(saezlab/OmnipathR) devtools::install_github("saezlab/progeny") Bio…

6 最大积水量

蛮力求解 #include <iostream> using namespace::std; using std::cout; using std::cin; int zdjsl(int n, int height[]) {int sum 0;int left_max[n];int right_max[n];left_max[0] height[0];right_max[n-1] height[n-1];for(int i1; i<n; i){left_max[i] m…

arcmap + oracle11g 迁移数据 报错 copyFeatures失败

原因排查&#xff1a; 1.通过这个界面&#xff0c;我们无法查到真正的原因&#xff0c; 2.将数据拷贝到我们自己的arcmap服务器中&#xff0c;采用 单个要素 导入&#xff0c;从result面板中查找原因&#xff1b; 从上面这个图中&#xff0c;看到关键信息 DBMS error ORA-016…

C++ STL——栈和队列(stack queue)

本节目标 1.stack的介绍和使用及其模拟实现 2.queue的介绍和使用及其模拟实现 3.priority_queue的介绍和使用及其模拟实现 4.容器适配器 1.stack的介绍和使用及其模拟实现 1.1 stack的介绍 stack的文档介绍 根据stack的文档介绍可以知道&#xff0c;stack是一种容器适配器…

关于“Python”的核心知识点整理大全29

目录 11.2.4 方法 setUp() 注意 11.3 小结 第二部分 项目1 外星人入侵 第&#xff11;2 章 武装飞船 注意 12.1 规划项目 12.2 安装 Pygame 注意 12.2.1 使用 pip 安装 Python 包 注意 如果你启动终端会话时使用的是命令python3&#xff0c;那么在这里应使用命令…

【python VS vba】(10) 在python使用matplotlib库来画不同的图形

7 下面是不同类型的图形 6 比如 散点图 sactter import numpy as np import matplotlib.pyplot as plt# 散点图 # x, y x np.random.normal(0, 1, 20) y np.random.normal(0, 1, 20)# 绘制散点图 plt.scatter(x, y, s25, alpha0.75)plt.xlabel("X") plt.ylabel(&…

Linux多版本cuda切换

目标 将cuda版本从10.0切换为11.1 步骤 查看当前cuda版本&#xff1a; nvcc -V编辑.bashrc文件&#xff1a; vim ~/.bashrc在文件中添加以下几行&#xff08;若已存在则忽略&#xff09;&#xff1a; export PATH$PATH:/usr/local/cuda/bin export LD_LIBRARY_PATH$LD_LI…

全网好听的BGM都在这里下载,赶紧收藏好了

无论是自媒体创作者还是从事视频剪辑工作的朋友&#xff0c;对于BGM的选择都很重要&#xff0c;一首适配的BGM能大大提升你作品的质量&#xff0c;还能让作品更优秀。哪里才能找到好听又免费的BGM&#xff1f;下面推荐几个我多年收藏的6个音效、音频素材网站&#xff0c;赶紧收…