java中多线程的基础知识

news2024/11/23 23:13:21

Process与Thread:

程序是指一段静态的代码,是指令和数据的有序集合,其本身没有任何运行的含义,它能够完成某些功能,它是应用软件执行的蓝本,它是一个静态的概念

进程是关于某个数据集合的一次运行活动,它是操作系统动态执行的基本单元,也是程序的一次动态执行过程,进程经历了从代码加载、运行到执行完毕的一个完整的过程,这个过程也是进程本身从产生、发展到最终消亡的过程。

进程的特点:

进程具有动态性、并发性、独立性和异步性

(1)动态性:进程的实质是进程实体的执行过程。
因此,动态性就是进程的最基本特性,动态性还表现在“它由创建而产生,由调度而执行,由撤销而消亡”
(2)并发性:是指多个进程实体同时存于内存中,且能在一段时间内同时运行
(3)独立性:进程实体是一个能独立运行、独立获得资源和独立接收调度的单位
(4)异步性:进程按照各自独立、不可预知的速度向前推进。

由于多个进程在并发执行时共享系统资源,致使它们在运行过程中呈现间断性的运行规律,所以进程在其生命周期中存在多种状态

一般情况下,每一个进程应该处于就绪(Ready)状态、执行(Running)状态和阻塞(Block)状态这3种状态中的一种

线程:

线程是进程中划分出来的更小的执行单位一个进程在其执行过程中可以产生多个线程,每个线程代表一条执行线索,如下图所示:
在这里插入图片描述

每个线程都有它各自产生存在和消亡的过程

一个进程中所产生的多个线程之间共享由操作系统分配给所属进程的内存资源(包括代码和数据),通过这些共享资源来实现数据交换、实时通信和同步操作。

但与进程不同的是,线程的中断与恢复可以节省系统的开销,通过多线程进行程序设计可以更好地描述并解决现实世界中的问题,现实世界中的某个活动需要多个对象同时参与并完成,一个线程个体就可以描述现实中的一个对象。

线程是CPU调度和执行的的单位

注意:
很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器,如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局.

java中的多线程:

Java语言内置了对多线程的支持,开发人员通过创建 Thread 或其子类对象就可以快速地创建线程对象并使用。

通过在一个进程中创建多个线程并执行多个线索,可以使得开发人员很方便地开发出具有多线程功能、能同时处理多个任务的功能强大的应用程序。

虽然执行线程给人一种几个事件同时发生的感觉,但实际上计算机在任何给定的时刻只能执行多个线程中的某一个线程。但由于 Java 虚拟机能够非常快速地将执行的控制权由一个线程切换到另一个线程,使得这些线程可以轮流执行,给人们的感觉就好像是这些线程同时执行一样

主线程[main线程]:

Java应用程序的入口是 main方法,当JVM加载main方法所属的字节码后,就会启动一个线程执行main方法,这个线程我们称为主线程,也是缺省的主线

假如 main 方法中再没有创建并启动其他的线程,那么主线程就从 main 方法的第一句代码开始执行,一直到main 方法中的最后一句代码执行完毕,此时主线程就结束了,应用程序也就结束了。

如果main 方法中创建并启动了其他的线程对象,此时JVM 就会在主线程和其他的线程之间轮流切换,确保每个线程都有机会使用 CPU 资源,当main 方法的最后一句代码执行完毕,此时只是主线程结束执行,Java 应用程序并没有结束,只有当其他所有的线程都执行完毕,此时 Java应用程序才会结束执行

Java 多线程的调度策略:

Java 虚拟机的一项任务就是负责线程的调度,线程的调度是指按照特定的机制为多个线程分配 CPU 的使用权,有两种调度模型:分时调度模型和抢占式调度模型,分时调度模型是指让所有线程轮流获得 CPU 的使用权,并且平均分配每个线程占用CPU 的时间片。

Java 虚拟机采用抢占式调度模型,是指优先让等待队列中处于就绪态的高优先级的线程对象获取 CPU使用权(JVM 规范中规定每个线程都有优先级,且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片,可能是优先级高的得到越多的执行时间片;反之,优先级低的分到的执行时间少,但不会分配不到执行时间)。

如果等待队列中线程的优先级相同,那么就随机选择一个线程,使其占用 CPU,处于运行状态的线程会一直执行,直至它不得不放弃CPU一个线程会因为以下原因放弃 CPU:

(1)Java虚拟机让当前线程暂时放弃 CPU,转到就绪态,让其他线程获得运行机会
(2)当前线程因为某些原因而处于阻塞状态
(3)线程运行结束

注意:

(1)线程的调度不是跨平台的,它不仅取决于 Java 虚拟机,还依赖于操作系统,在某些
操作系统中,即使运行中的线程没有遇到阻塞,也会在运行一段时间后放弃 CPU,给其他线
程运行的机会。
(2)Java 线程的调度不是分时的,同时启动多个线程后,不能保证各个线程轮流获得均等
的时间片

线程的状态与生命周期:

和其他有生命的物质一样,线程也有它完整的生命周期,Java 语言使用 Thread 类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历新建态、就绪态、运行态、阻塞态和消亡态这几种状态。下图展示了线程生命周期中状态的转换关系

在这里插入图片描述1.新建状态:

通过 new 关键字创建一个线对象后,就处于新建状态

此时,线程被分配内存空间,数据已经被初始化,但该线程对象还未被激活,不能获取 CPU 使用权。

注:此时也可以把该线程对象变成消亡状态,但一般不会这样做。

2.就绪状态:

处于新建状态的线程调用 start()方法可以将线程的状态转换成就绪状态

进入就绪状态的线程对象就会进人 CPU 的等待队列,等待获取 CPU 的使用权,此时该线程对象已具备了运行的条件但还没有真正运行。

3.运行状态:

当处于就绪状态的线程被调度并获得 CPU 的使用权后,线程对象便进人运行状态在运行状态执行线程对象的 run()方法,这个方法是线程对象最重要的方法,因为该方法定义了线程对象需要完成的任务

4.阻塞状态:

在某种特殊情况下线程对象需要让出CPU的使用权,暂停 run()方法的执行,便进入阻塞状态,让线程进人阻襄状态的原因通常有以下几个:

(1)JVM将CPU使用权从当前线程对象切换到另一个线程对象
(2)当前线程对象执行了sleep(int millsecond)方法进人阻塞状态,当millsecond时
间过后,线程对象就会重新“苏醒”,重新进人就绪状态,等待下一次获得 CPU的使用权,接
着执行剩余的run()方法
(3)线程对象在运行状态时执行了 wait()方法使得当前线程进人等待(阻塞)状态进入等
待状态的线程对象不会主动进入就绪队列排队等待 CPU资源,必须由其他线对象调用
notify()将线程对象从等待状态“唤醒”被“唤醒”的线程对象才能重新进入就绪队列排队
等待CPU资源
(4)线程对象在运行状态执行了其他导致其进入阻塞状态的方法,例如 IO 操作。当引起
阻塞的原因消除时,线程才重新进人到就绪队列中等待 CPU 资源,以便从原来阻塞处的
代码往后继续执行
(5)消亡(死亡)状态处于消亡状态的线程已经不具备运行的能力,等待被JVM 垃圾收集器
回收并释放内存。当线程对象执行完run()方法中的最后一句代码或者被强制性地终止
run()方法的执行后,线程对象就会进人消亡状态。

Thread:

java.lang.Thread类及其子类对象表示一个线程对象,由于java.lang 包自动被导人每个java程序中,所以在使用Thread时,无需在java程序中编写import语,一个类在继了Thread类之后,就具备了多线程的操作功能

使用继承Thread的方法创建并执行一个线程,需要执行以下4个步骤:

定义类继承Thread类,并扩展新的功能;
重写run()方法
通过new关键字实例化一个新对象;
调用start()方法启动线程

线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。

注:线程不一定立即执行,CPU安排调度

创建线程的方式1:继承Thread类

Thread()	//创建一个新的线程对象,采用默认的名称
Thread(String name)	//创建一个名为name的线程对象
Thread(Runnable runner)	//创建以实现Runnable接口的类的对象为参数的线程对象
Thread(Runnable runner,String name)	//创建以实现Runnable接口的类的对象为参数的线程对象,线程名为name

举例:

package Thread;


//编写教师类线程
public class TeacherThread extends Thread{
    public TeacherThread(String str) {
        super(str);//调用父类构造方法给线程起名
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            //Thread.currentThread().getName()获取当前线程的名字
            System.out.println("当前线程是->" + Thread.currentThread().getName() + "我要布置作业");
        }
    }
}

//编写学生类线程
class Student_Thread extends Thread{
    public Student_Thread(String name) {
        super(name);//调用父类构造方法给线程起名
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("当前线程是->" + Thread.currentThread().getName() + "我要好好学习");
        }
    }
}

//测试类
class Thread_test extends Thread{
    public static void main(String[] args) {
        TeacherThread teacherThread=new TeacherThread("Lisa");
        teacherThread.start();  //启动教师线程
        Student_Thread student_thread=new Student_Thread("小明");
        student_thread.start();   //启动学生线程
    }
}

输出:

当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习
当前线程是->Lisa我要布置作业
当前线程是->小明我要好好学习

以上两种方法的输出结果不同的原因如下:

普通方法调用和多线程:

在这里插入图片描述

以下实例中包含了Thread各个方法:

package Thread;

public class Thread_test {
    public static void main(String[] args) throws InterruptedException {
    Thread_test thread =new Thread_test();
    thread.start(); //start()方法启动线程
    thread.sleep(10);   //sleep()方法线程休眠一段时间,单位为毫秒,线程进入阻塞状态
    thread.run();   //run()方法使线程获得CPU使用权后所执行的方法
    thread.interrupt(); //中断线程
    System.out.println(thread.currentThread()); //返回当前正在使用CPU的线程对象
    thread.yield(); //将CPU控制权主要移交到下一个线程
    System.out.println(thread.isAlive());   //判断当前线程对象是都还存活,只有处于就绪状态,运行态和阻塞态的线程对象返回true
    thread.setPriority(2);  //设置线程对象的优先级,n的取值为1-10
    thread.join();  //在当前线程中等待调用此方法的线程执行完毕
    thread.join(10);    //在当前线程中等待调用此方法的线程执行,等待时间为x毫秒
    }
}

Thread有关方法的简单应用:

例一:

package Thread;

public class Thread1 extends Thread{

//第二步---->thread1线程获得CPU使用权后休眠10毫秒并执行10次循环
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=0;i<10;i++){
            System.out.println("当前线程是->" + Thread.currentThread().getName() + "正在运行");
        }
    }
}
class Thread1_test{
    public static void main(String[] args) throws InterruptedException {
//第一步---->主线程启动thread1线程后,主线程休眠了5秒钟,这五秒钟主线程放弃了CPU的使用权
        Thread1 thread1=new Thread1();
        thread1.start();    //启动thread1线程
        try {
            Thread.sleep(5000);//主线程休眠5秒钟
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
//第三步---->当main线程结束休眠并重新获得CPU使用权后,thread1线程已经在main线程休眠的时间内执行完了自己的run方法并进入了消亡状态
        if(thread1.isAlive())//判断thread1线程是否还存活---->在第三步时,已经thread1已经进入消亡状态
        {
            System.out.println("线程thread1还存活");

            try {
                thread1.join();//主线程无条件等待thread1,线程执行完它的run方法
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("main线程运行结束");
    }
}

输出:

当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
当前线程是->Thread-0正在运行
main线程运行结束

例二:

package Thread;

public class Thread2 extends Thread{
    public Thread2(String name) {
        super(name);
    }

    @Override
    public void run() {
    //第一步--->main 线程启动 thread2 线程后进入休眠状态让出 CPU使用权,此时thread2线程获得CPU使用权,并休眠1秒钟又让出CPU使用权
        try {
            Thread.sleep(1000);	//休眠一秒钟
        } catch (InterruptedException e) {
        //第三步--->thread2 线程被中断后执行InterruptedException 异常的catch代码块中逻辑
            System.out.println(Thread.currentThread().getName()+"的休眠状态被打断");
            return ;//从run方法返回
        }
        //上述被中断,所以结果中输出“线t1的休眠被打断”,并从 run()方法返回。t2线程在循环变量i为5时执行 yield()方法
        for(int i=0;i<10;i++){
            System.out.println("当前线程:"+Thread.currentThread().getName()+"正在运行");
            if(i==5){
                Thread t=Thread.currentThread();
                t.yield();	//当前线程执行让步操作
                System.out.println(t.getName()+"执行让步操作");
            }
        }
    }
}
class Thread2_test{
    public static void main(String[] args) throws InterruptedException {
    
        Thread2 thread2=new Thread2("线程1");
        Thread2 thread2s=new Thread2("线程2");
        thread2.start();	//启动thread2线程
        Thread.sleep(5);
        
		//第二步--->main 线继续执行启动 t2线程并执行t线程的interrupt()方法
        thread2s.start();	//启动thread2s线程
        thread2.interrupt();//对thread2线程执行中断操作
    }
}

输出:

线程1的休眠状态被打断
当前线程:线程2正在运行
当前线程:线程2正在运行
当前线程:线程2正在运行
当前线程:线程2正在运行
当前线程:线程2正在运行
当前线程:线程2正在运行
线程2执行让步操作
当前线程:线程2正在运行
当前线程:线程2正在运行
当前线程:线程2正在运行
当前线程:线程2正在运行

通过实现 Runnable 接口实现线程:

由于Java单继承的限制,当一个类已经继承了一个类(不是Thread类),但这个类还想要实现线程的功能,此时已经不能再继承Thread类。在这种情况下这个类可以实现 Runnable接口

Runnable接口只包含一个run()方法,代码如下所示:

class MyThread implements Runnable{
public void run(){
//线程对象被调度后所执行的方法
	}
}

利用Runnable接口创建并运行线程的编程步骤如下:

(1)创建一个实现了Runnable接口的类的对象调用Thread类带 Runnable形参的造方法,把 Runnable接口实现类对象传递给 Thread类的对象,为新线程提供代码和数据:

MyThread tl=new MyThread ();//MyThread实现了Runnable接口
Thread t2= newThread(t1);

(2)使用线程对象调用start()方法启动线程:

package Runnable;


//教师线程实现Runnable接口
public class Runnable_test implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println("当前线程是->"+Thread.currentThread().getName()+"我要布置作业");
        }
    }
}



//学生线程实现Runnable接口
class Runnable_tests implements  Runnable{
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println("当前线程是->"+Thread.currentThread().getName()+"我要好好学习");
        }
    }
}



//测试类
class test{
    public static void main(String[] args) {
        //创建实现Runnable接口实现类的实例化对象
        Runnable_test runnable_test=new Runnable_test();
        Runnable_tests runnable_tests=new Runnable_tests();

        //为线程命名并启动线程
        Thread thread=new Thread(runnable_test,"教师线程");
        thread.start();
        Thread threads=new Thread(runnable_tests,"学生线程");
        threads.start();
    }
}

输出:

当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->教师线程我要布置作业
当前线程是->学生线程我要好好学习
当前线程是->学生线程我要好好学习

由于 Runnable接口中的run()方法没有返回值,在某些实际应用中需要线程对象有返回值,所以在java.util.concurrent 包中提供了一个Callable<V>接口,该接口提供了一个call()方法,其中V表示 call()方法的返回值,而且可以抛出异常

语法如下:

V call() throws Exception;

因为Thread类的构造方法中不包含Callable接口类型的参数,所以实现Callable接口的对象无法直接与Thread类进行联系,因此中间需要借助 FutureTask类,FutureTask类实现了Runnable接口,FutureTask类的构造方法可以接受Callable接口类型的参数

FutureTask类位于java.utilconcurrent包中,用来表示一个异步计算的结果,通过该类的get()方法可以得到Callable接口中calI()方法的返回值

举例:

package Runnable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//实现Callable接口
public class Mytask implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Integer sum=0;
        for(int i=0;i<=100;i++){
            sum+=i;
        }
        return sum;
    }
}


 class Mytask_test{
     public static void main(String[] args) throws ExecutionException, InterruptedException {
         //创建FutureTask类的对象,参数为Callable接口类型
         FutureTask<Integer> futureTask=new FutureTask<>(new Mytask());
         //创建线程对象,参数类型是FutureTask,因为FutureTask实现了Runnable
         Thread mythread=new Thread(futureTask);
         mythread.start();
         //得到线程对象的计算结果,也就是call()方法的返回值
         Integer result=futureTask.get();
         System.out.println("线程的运算结果是:"+result);
     }
 }

输出:

线程的运算结果是:5050

建立线程方法的比较:

继承Thread类

这种方式创建一个类继承 Thread类,并且覆盖了Thread()类的 run()方法,描述线程对象所执行的操作

优点是: Thread 类的子类对象就是线程对象,能够使用 Thread 类声明的方法且具有线程体。

缺点是:不能实现多继承。

实现Runnable接口

当一个类已经继承了一个类,但还想要以线程方式运行时,就需要实现 Runnable 接口,一个实现 Runnable接口的类的对象本身不是线程的对象。它作为一个线程对象的目标对象使用,因此,使用时需要声明一个 Thread 类对象,可以实现资源的共享.

实现Callable 接口

Callable 接口的 call()方法可以有返回值,而且能够抛出异常。Callable 接口无法直接作为参数来创建线程对象,需要借助 FutureTask类将实现 Callable 接口的类的对象作为参数创建FutureTask对象,再将 FutureTask 对象作为参数创建线程对象,所以实现 Callable接口的类的对象也可以作为线程对象的目标对象使用

注意:

当一个类只为了修改run()方法,而其他方法不变的情况下,推荐使用 Runnable接口的
方式;

当一个类继承了另外一个类,而又想实现多线程的情况下,可以使用实现Runnable 接口
的方式,也可以使用实现 Callable 接口的方式(此方式可以得到子线程的运行结果),因
为Java 不支持多继承;

当多个线程要共享同一个资源时,推荐使用Runnable接口的方式。

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

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

相关文章

C++中二叉树的递归遍历方法2-2

在《C中二叉树的递归非遍历方法3-3》中提到&#xff0c;二叉树的层序遍历的输出顺序是从根节点开始&#xff0c;一层一层横向遍历各个节点。如图1所示的二叉树&#xff0c;层序遍历的输出的输出顺序为“1->2->3->4->5->6”。 图1 二叉树结构 1 递归实现层序遍历…

计算机视觉 基于CUDA编程的入门与实践 线程及同步一

一、并行执行规模 CUDA关于并行执行具有分层结构。每次内核启动时可以被切分成多个并行执行的块&#xff0c;而每个块又可以进一步地被切分成多个线程。这种并行执行的副本可以通过两种方式完成&#xff1a;一种是启动多个并行的块&#xff0c;每个块具有1个线程&#xff1b;另…

项目实战之旅游网(五)后台角色管理(下) 后台权限管理

目录 一.后台角色管理&#xff08;下&#xff09; 1.查询角色权限 2.修改角色权限 3.优化侧边栏菜单 二.后台权限管理 1.权限列表 2.新增权限 3.修改权限 4.删除权限 一.后台角色管理&#xff08;下&#xff09; 1.查询角色权限 先新建一个bean类型的实体类&#xf…

单商户商城系统功能拆解53—数据分析

单商户商城系统&#xff0c;也称为B2C自营电商模式单店商城系统。可以快速帮助个人、机构和企业搭建自己的私域交易线上商城。 单商户商城系统完美契合私域流量变现闭环交易使用。通常拥有丰富的营销玩法&#xff0c;例如拼团&#xff0c;秒杀&#xff0c;砍价&#xff0c;包邮…

一文清晰带你弄清楚Spring IOC 循环依赖问题是如何解决的

什么是循环依赖 循环依赖又被成为循环引用,即两个或者多个bean相互之间的持有对方,比如A 引用B,B引用C,C 又引用A,则它们最终反映为一个环,如下图所示: 循环依赖是对象之间的相互依赖关系,循环依赖就是一个死循环,除非有终结条件,否则就是死循环,最终导致内存溢出错误. 解决…

【Java 数据结构】优先级队列

篮球哥温馨提示&#xff1a;编程的同时不要忘记锻炼哦&#xff01;谁是你的优先级呢&#xff1f; 目录 1、优先级队列 1.1 优先级队列概念 1.2 堆的概念 1.3 堆的存储结构 2、模拟实现优先级队列 2.1 成员变量的设定 2.2 根据数组构造出一个堆 2.3 向下调整 2.4 creat…

电压放大器如何测试线性稳压器

有不少的电子工程师咨询电压放大器如何测试线性稳压器&#xff0c;那么这种要怎么做呢&#xff0c;下面让安泰电子来为大家介绍。 一、什么是低压差线性稳压器&#xff1f; 低压差线性稳压器是集成电路稳压器&#xff0c;经常用来电流主通道控制&#xff0c;芯片上集成导通电阻…

SQL 事务基础

事务基础 1 事务概念 所谓事务就是用户定义的一个数据库操作序列&#xff0c;这些操作要么全做&#xff0c;要不全不做&#xff0c;是一个不可分割的工作单位。 2 事务的特性&#xff08;ACID&#xff09; 原子性&#xff08;atomicity&#xff09; 事务是数据库工作的逻辑…

数据,信息,知识,智慧

数据&#xff0c;信息&#xff0c;知识&#xff0c;智慧 知识管理的对象有数据、信息、知识、智慧&#xff0c;而不仅仅是知识。将这些联系起来处理&#xff0c;就能期待综合效果。 作为知识资产的知识 传统的资源以人、物、钱为代表。但是&#xff0c;在经济活动的现场&…

2023年全国管理类联考英语二真题及解析

Section Ⅰ Use of English Here’s a common scenario that any number of entrepreneurs face today: you’re the CEO of a small business and though youre making a nice 1 , you need to find a way to take it to the next level. what you need to do is 2 growth by …

MobPush:社交app硝烟再起,如何突出重围?

推送&#xff0c;能够在产品和用户之间建立有效的连接。好的推送能够传达有价值的信息和提供好用的功能&#xff0c;让企业和用户沟通&#xff0c;把准确的信息第一时间传达。然而很多企业都没有意识到这一点&#xff0c;对于推送的频率&#xff0c;内容&#xff0c;以及针对各…

辗转相除法求最大公因数-C语言

辗转相除法&#xff0c;又名欧几里德算法&#xff0c;是求最大公约数的一种方法。以除数和余数反复做除法运算&#xff0c;最终当余数为0时&#xff0c;取当前算式除数为最大公约数。 例1&#xff1a;求2015和15的最大公因数。 2015 15 * 134 5 15 5 * 3 0 因此&#xff0…

亚马逊云科技 Build On - 咖啡厅Demo学习stepfunction serverless应用

荣幸参与和csdn和aws联合举办的buildon实验活动&#xff0c;主要目的还是学习stepfucntion的使用&#xff0c;这个服务能够集成大量aws service感觉可以出现很多有趣的用法。官方给出的文档已经非常详细了&#xff0c;这里只是对一些比较难理解的点进行了记录和解释&#xff0c…

restricted isometry property 稀疏 (CSDN_0002_20220908)

目录 1. 稀疏问题的引出 2. RIP 说明&#xff1a; 1. 由于参考多篇文献&#xff0c;所以本文的符号与原文略有不同。 2. 由于原文公式较多&#xff0c;所以本文采用了截图的形式&#xff0c;如需要电子版文档&#xff0c;可私信或留言。 1. 稀疏问题的引出 2. RIP 关于1-…

MySQL存储过程高级SQL语句总结

MySQL高级SQL语句&#xff08;存储过程&#xff09; 一、存储过程的概述 1.1 什么是存储过程 存储过程是一组为了完成特定功能的SQL语句集合。 存储过程在使用过程中是将常用或者复杂的工作预先使用SQL语句写好并用一个指定的名称存储起来&#xff0c;这个过程经编译和优化后…

Quarkus构建一个原生可执行文件

先决条件 大概15分钟 编辑器 安装GraalVM&#xff0c;并正确配置 Apache Maven 3.8.1 可以工作的容器 (Docker或者Podman) 一个 C语言工作开发环境 Quarkus应用程序代码 支持在C语言中进行原生编译 拥有一个C语言工作开发者环境意味着什么&#xff1f; 在Linux上&#xf…

华为手机恢复出厂设置后如何恢复数据

当您恢复出厂设置时&#xff0c;手机上存储的所有数据都会被清空。这是因为恢复出厂设置基本上是您从头开始设置手机的一种方式。 众所周知&#xff0c;重置手机会清除手机上的现有数据。如果这种强制删除让你丢失了重要数据&#xff0c;那么恢复出厂设置后数据还能恢复吗&…

企企通:如何利用数字化之道,赋能汽车行业供应链创新?

汽车是国民经济的支柱性企业&#xff0c;产业链长&#xff0c;涉及面广、带动性强&#xff0c;国际化程度高&#xff0c;在全球主要经济大国的产业体系中一直占据着重要地位。 我国汽车行业通过几十年的高速发展之后&#xff0c;从量变到质变&#xff0c;逐渐向低速增长的模式开…

把TeamTalk(即时通讯项目)中的线程池连接池拆出来单独测试。

研究过Teamtalk的伙伴会发现它的线程池和连接池与很多文件有关联&#xff0c; 这篇文章主要写&#xff0c;把它的线程池连接池拆出来需要用到哪些文件。 其实我本来只想测试它的连接池的&#xff0c;但发现连接池里套的有线程池&#xff0c;于是就一起拆出来了。 整个工程的树…

基于SpringBoot的社区小型图书管理系统的设计与实现

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…