多线程原理详解01(程序、进程、线程介绍,线程创建的三种方式(Thread、Runnable、Callable)、三种方式各自实现多线程的具体操作、代码解析)

news2024/12/28 8:05:29

目录

  • 多线程原理详解
    • 01_线程简介
      • 多任务
      • 多线程
      • 程序、进程、线程
      • Process(进程)与 Thread (线程)
      • 总结:
    • 02_线程创建三种方式:
      • 1、继承 Thread 类
        • 1-1:通过继承Thread类实现多线程
          • 演示代码
        • 1-2:演示多线程同步下载图片:
          • 演示代码
      • 2、实现 Runnable 接口 (推荐)
        • 2-1:通过实现Runnable接口来实现多线程
          • 具体代码
        • 2-2:通过实现Runnable接口来实现多线程同步下载图片
          • 具体代码
        • 2-3:演示多个线程同时操作同一个对象出现的并发问题
          • 演示买火车票的例子
            • 具体代码
          • 通过 synchronized 关键字同步代码块解决并发问题
            • 具体代码
        • 2-4:演示龟兔赛跑案例
          • 用多线程的方式模拟龟兔赛跑代码分析
          • 具体代码
      • 3、实现 Callable 接口
        • 演示多线程同时下载图片
        • 具体代码
      • 4、总结:三种创建方式的区别:

多线程原理详解


01_线程简介

任务、进程、线程、多线程


多任务

就是在一个时间内做很多事情,比如边吃饭变玩手机,但是本质上,在那一秒内,其实只是在玩手机,或者在一秒内只是在吃饭。
看起来像是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件事情而已

在这里插入图片描述


多线程

比如原本一条道路看成一个线程,多辆车都在一个道路上形式,容易出问题。
但是如果给道路划分路线,相当于弄多条线程,让多辆车在各自的线程行驶,出问题的概率就变小。
在这里插入图片描述

平常项目中的普通的方法调用,基本是一条线程走到底,即使调用方法,也是在方法执行完后继续往下走,只用到主线程而已。

多线程就是多条线程各自执行,效率更高。

就像一个大超市,只有一个收银员(单线程)和多个收银员(多线程)的区别。

在这里插入图片描述


程序、进程、线程

在操作系统中运行的程序就是进程,比如微信、QQ、腾讯视频、游戏这些就是程序,也就是进程

一个进程(腾讯视频)可以有多个线程(视频声音、图像、弹幕等)

在这里插入图片描述


Process(进程)与 Thread (线程)

程序(静态的)–>程序跑起来变成进程(动态的)–>进程里面包含多个线程,真正执行功能的就是这些线程

在这里插入图片描述


总结:

在这里插入图片描述

注意:

单核 CPU 上所谓的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了。

多核 CPU 上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核 CPU 的优势来,达到充分利用 CPU 的目的



02_线程创建三种方式:

三种创建方式主要是:继承 Thread 类、实现 Runnable 接口,还有一个是 实现 Callable 接口

最好的是实现 Runnable 接口,因为实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度。

这三种方式都能开启多个线程来执行业务逻辑。


1、继承 Thread 类

步骤:

1、自定义一个类,继承 Thread 类;
2、重写 run() 方法,编写线程执行体;
3、创建线程对象,调用 start() 方法启动线程;


解释:

自定义一个类去继承Thread 类,作为一个线程类对象,然后就可以重写Thread类的 run() 方法;
接着在main主线程里面,可以通过线程对象去调用 start() 方法,该方法的作用是让 java 虚拟机去启动一个新的线程去执行 run() 方法里面的代码。
然后主线程继续执行自己的业务代码。

这样主线程和新的线程就都同时执行,这个就是多线程操作。


1-1:通过继承Thread类实现多线程

当我们调用 start() 方法后,start() 方法会通知 Java 虚拟机启动一个新线程(也就是子线程),并在新线程中调用该线程对象(t1)的 run() 方法。

而主线程调用start() 方法后,也会继续执行下面的代码。

这个时候,主线程和新线程就会并发执行,这样就演示出了多线程并发执行的特性。

在这里插入图片描述


演示代码

//创建线程方式一:继承 Thread 类, 重写 run() 方法,调用 start() 方法开启线程
//注意:线程开启不一定立即执行,由 CPU 调度执行
public class TestThread01 extends Thread
{
    //重写 run() 方法
    @Override
    public void run()
    {
        //run 方法线程体
        for (int i = 0; i < 20; i++)
        {
            System.err.println("run方法线程:bbbbbb--->" + i);
        }

    }

    //main 主线程
    public static void main(String[] args)
    {
        //创建一个线程对象
        TestThread01 t1 = new TestThread01();

        //直接调用run方法,只是普通的调用,跟多线程没有关系
        //t1.run();

        //调用start()方法开启线程,主线程和run()方法的线程就会同时进行
        t1.start();

        for (int i = 0; i < 20; i++)
        {
            System.err.println("main主线程:aaaaaaaaaa--->" + i);
        }
    }

}

1-2:演示多线程同步下载图片:

先下载一个 Apache 的 Commons IO 的 jar 包,是一个文件下载的工具类库

在这里插入图片描述

下载之后解压,把这个 jar 包拷贝到项目里面去

在这里插入图片描述

点击右键,点击【Add library】添加进去即可。
有了这个工具类库,才可以使用比如 FileUtils 这样的工具类

在这里插入图片描述


代码实现:

代码中,可以看出如果是单线程下载这3张图片的话,下载顺序应该是1、2、3
可从结果看,下载顺序是3、2、1,说明是多线程在同步下载图片,哪条线程快就哪条先下载完。

在这里插入图片描述


如图,图片成功下载下来了,这图片是我其他 博客文章里面的图片

在这里插入图片描述


演示代码
package cn.ljh.threaddemo.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

// 实现多线程同步下载图片
public class TestThread02 extends Thread
{
    private String url; //网络图片地址
    private String name;//保存的文件名

    //构造器
    public TestThread02(String url ,String name){
        this.url = url;
        this.name = name;
    }

    //重写run方法
    @Override
    public void run()
    {
        //run方法里面调用了文件下载的downloader方法
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.err.println("下载了文件名为:"+ name);
    }

    //主线程
    public static void main(String[] args)
    {
        //创建三个线程对象
        TestThread02 t1 = new TestThread02(
                "https://img-blog.csdnimg.cn/direct/0aded64215424e0488d3da76357750b6.png",
                "1.jpg");
        TestThread02 t2 = new TestThread02(
                "https://img-blog.csdnimg.cn/direct/0aded64215424e0488d3da76357750b6.png",
                "2.jpg");
        TestThread02 t3 = new TestThread02(
                "https://img-blog.csdnimg.cn/direct/0aded64215424e0488d3da76357750b6.png",
                "3.jpg");
        //启动3条线程
        t1.start();
        t2.start();
        t3.start();
    }
}


//图片下载器
class WebDownloader{

    //下载方法
    public void downloader(String url,String  name){
        try
        {
            //把网络图片地址url变成一个文件,参数1:网络图片地址 ;参数2:下载后保存的文件名
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e)
        {
            e.printStackTrace();
            System.err.println("IO异常,downloader 方法出现问题");
        }
    }

}


2、实现 Runnable 接口 (推荐)

步骤:

1、自定义一个类,实现 Runnable 接口;
2、实现 run() 方法,编写线程执行体;
3、创建线程对象,调用 start() 方法启动线程;


2-1:通过实现Runnable接口来实现多线程

这个和上面继承 Thread 类的操作差别不大。

如图:这个实现了Runnable接口的自定义线程类TestThread03,作为参数传进到 Thread 这个线程对象里面,然后通过 Thread 这个线程对象调用 start() 方法启动新线程。

解释下这个自定义线程类TestThread03,为什么说它是一个代理对象。

详细解释:

代码中:
将 t 视为一个代理对象的原因是它充当了线程对象(thread)和具体任务逻辑之间的中介。

具体来说,t 自定义线程类对象 实现了 Runnable 接口,并在其 run() 方法中定义了线程要执行的任务逻辑。

当我们通过 Thread 类创建一个 thread 对象时,需要将一个实现了 Runnable接口的对象(t)作为参数传入,那么这个对象(t)就是线程对象(thread)的代理,它 (t) 负责管理线程的生命周期和执行任务的逻辑。

在调用 thread.start() 方法后,线程对象(thread)会自动调用代理对象(t)的 run() 方法,从而执行具体的任务逻辑。

t 作为一个代理对象,充当了线程对象(thread)和具体任务逻辑之间的桥梁,使得线程的管理和任务的执行能够分开进行,提高了代码的灵活性和可维护性。

简单来说:

实现了Runnable接口的t对象,它实现了run()方法,作用是在run()方法里面写具体的业务逻辑代码。

而创建一个 Thread 对象,作用是用来调用 start() 方法从而启动新线程。

而启动的新线程要执行的业务逻辑代码在t对象的run()方法里面,所以就把 t 作为代理对象传给 thread 对象。

这样就是通过代理的方式实现了线程的启动(thread.start())和任务(任务在run()方法里面)的执行。

在这里插入图片描述

和上面的继承 Thread 类差不多。

再简洁的说法:

继承 Thread 类的自定义线程类对象,是自身调用 start 方法启动新线程。

实现Runnable 接口的自定义线程类对象,是作为创建Thread线程对象的参数,由Thread线程对象来调用start方法启动新线程


具体代码
package cn.ljh.threaddemo.demo01;

//创建线程方式2:实现 Runnable 接口,重写 run 方法,执行线程需要丢入 runnable 接口实现类,调用 start 方法

public class TestThread03 implements Runnable
{
    //实现 run() 方法
    @Override
    public void run()
    {
        //run 方法线程体
        for (int i = 0; i < 20; i++)
        {
            System.err.println("run方法线程:bbbbbb--->" + i);
        }
    }

    //main 主线程
    public static void main(String[] args)
    {
        //创建 Runnable 接口的实现类对象
        TestThread03 t = new TestThread03();

        //创建线程对象,通过线程对象thread来开启我们的线程---代理对象t
        Thread thread = new Thread(t);

        thread.start();

        for (int i = 0; i < 20; i++)
        {
            System.err.println("main主线程:aaaaaaaaaa--->" + i);
        }
    }
}


2-2:通过实现Runnable接口来实现多线程同步下载图片

小小区别:

在这里插入图片描述


具体代码
// 实现多线程同步下载图片
public class TestThread04 implements Runnable
{
    private String url; //网络图片地址
    private String name;//保存的文件名
    
    //构造器
    public TestThread04(String url ,String name){
        this.url = url;
        this.name = name;
    }
    
    //重写run方法
    @Override
    public void run()
    {
        //run方法里面调用了文件下载的downloader方法
        WebDownloader04 webDownloader = new WebDownloader04();
        webDownloader.downloader(url,name);
        System.err.println("下载了文件名为:"+ name);
    }
    
    //主线程
    public static void main(String[] args)
    {
        //创建三个线程对象
        TestThread04 t1 = new TestThread04(
                "https://img-blog.csdnimg.cn/direct/0aded64215424e0488d3da76357750b6.png",
                "1.jpg");
        TestThread04 t2 = new TestThread04(
                "https://img-blog.csdnimg.cn/direct/0aded64215424e0488d3da76357750b6.png",
                "2.jpg");
        TestThread04 t3 = new TestThread04(
                "https://img-blog.csdnimg.cn/direct/0aded64215424e0488d3da76357750b6.png",
                "3.jpg");
        //通过线程对象启动3条线程
        Thread thread = new Thread(t1);
        thread.start();
        new Thread(t2).start();
        new Thread(t3).start();
    }
}

//图片下载器
class WebDownloader04{
    //下载方法
    public void downloader(String url,String  name){
        try
        {
            //把网络图片地址url变成一个文件,参数1:网络图片地址 ;参数2:下载后保存的文件名
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e)
        {
            e.printStackTrace();
            System.err.println("IO异常,downloader 方法出现问题");
        }
    }
}


2-3:演示多个线程同时操作同一个对象出现的并发问题

演示买火车票的例子

演示多个线程同时操作同一个对象—以买火车票为例子

多个线程操作同一个资源的情况下,线程不安全,数据紊乱,
这个就是多线程情况下会出现的并发问题。

在这里插入图片描述


具体代码
//演示多个线程同时操作同一个对象---以买火车票为例子
//出现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread05 implements Runnable
{
    //票数
    private int ticketNums = 10;

    @Override
    public void run()
    {
        while (true){
            if (ticketNums<=0){
                //终止当前所在循环
                break;
            }
            //模拟延迟
            try
            {
                Thread.sleep(500);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            System.err.println(Thread.currentThread().getName()+"--->买到了第【 "+ticketNums-- + " 】张票");
        }
    }

    public static void main(String[] args)
    {
        TestThread05 t = new TestThread05();
        //启动新线程  参数1:t 就是代理对象   参数2:name 就是当前线程名字
        new Thread(t,"小黄").start();
        new Thread(t,"小白").start();
        new Thread(t,"小绿").start();
    }
}

通过 synchronized 关键字同步代码块解决并发问题

简单解决下上面买车票的并发问题:

使用 synchronized 保证同一时间只有一个线程能来操作这个代码块。

在这里插入图片描述


具体代码
package cn.ljh.threaddemo.demo01;


//演示多个线程同时操作同一个对象---以买火车票为例子
//出现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread05 implements Runnable
{
    //票数
    private int ticketNums = 10;

    @Override
    public void run()
    {
        while (true)
        {
            synchronized (this)
            {
                if (ticketNums <= 0)
                {
                    //终止当前所在循环
                    break;
                }
                System.err.println(Thread.currentThread().getName()
                        + "--->买到了第【 " + ticketNums-- + " 】张票");

            }
            //模拟延迟
            try
            {
                Thread.sleep(500);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args)
    {
        TestThread05 t = new TestThread05();
        //启动新线程  参数1:t 就是代理对象   参数2:name 就是当前线程名字
        Thread t1 = new Thread(t, "小黄");
        Thread t2 = new Thread(t, "小白");
        Thread t3 = new Thread(t, "小绿");

        t1.start();
        t2.start();
        t3.start();
    }
}

2-4:演示龟兔赛跑案例

自定义一个线程类,实现 Runnable 接口,然后启动两个线程(一个兔子线程和一个乌龟线程),用for循环,看哪个线程先执行到100米。

在这里插入图片描述


用多线程的方式模拟龟兔赛跑代码分析

在这里插入图片描述


具体代码

// 模拟龟兔赛跑案例
public class Race implements Runnable
{
    //胜利者
    private static String winner;

    //实现 run() 方法
    @Override
    public void run()
    {
        for (int i = 0; i <= 100; i++)
        {
            //模拟兔子休息--当前线程是"兔子" 并且 每跑10步,就休息一会
            if (Thread.currentThread().getName().equals("兔子") && i%10==0){
                try
                {
                    Thread.sleep(5);
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }

            //判断比赛是否结束
            boolean flag = gameOver(i);
            //如果flag为true,表示比赛结束,直接终止当前所在循环
            if (flag){
                break;
            }
            System.err.println(Thread.currentThread().getName()
                    + " ---> 跑了【 " + i + " 】步");
        }

    }
    
    //主线程
    public static void main(String[] args)
    {

        Race race = new Race();
        //启动名为兔子的新线程
        new Thread(race, "兔子").start();
        //启动名为乌龟的新线程
        new Thread(race, "乌龟").start();

    }
    
    //判断是否完成比赛的方法
    public boolean gameOver(int steps)
    {
        //判断是否有胜利者
        if (winner != null)
        {
            //已经存在胜利者了,返回true
            return true;
        } else if (steps >= 100)
        {
            winner = Thread.currentThread().getName();
            System.err.println("胜利者是:【 " + winner + " 】");
            return true;
        }
        return false;
    }
}


3、实现 Callable 接口

步骤:

在这里插入图片描述


演示多线程同时下载图片

演示的依然是这个多线程同时下载图片的功能,不过这里是实现 Callable 接口。

这里的多线程操作,是通过创建一个线程池,然后把实现了 Callable 接口的自定义线程类对象(t1、t2、t3)作为任务参数提交给线程池去执行,并且可以获取线程执行后的结果,最后关闭线程池。

t1、t2、t3各自代表一个任务,它们实现了Callable接口,并且在call()方法中定义了具体的任务逻辑。
提交t1、t2、t3任务给线程池后,线程池会负责安排线程来执行t1、t2、t3中定义的任务逻辑。

在这里插入图片描述

通过源码可以看出 ,Callable 接口里面只有一个 call() 方法需要重写
在这里插入图片描述


具体代码
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//线程创建方式三:实现 Callable 接口
/**
 * Callable 的好处
 * 1、可以定义返回值
 * 2、可以抛出异常
 */

// 实现多线程同步下载图片
public class TestCallable implements Callable<Boolean>
{
    private String url; //网络图片地址
    private String name;//保存的文件名

    //构造器
    public TestCallable(String url, String name)
    {
        this.url = url;
        this.name = name;
    }

    //实现 Call 方法
    @Override
    public Boolean call() throws Exception
    {
        //run方法里面调用了文件下载的downloader方法
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url, name);
        System.err.println("下载了文件名为:" + name);

        //返回true表示任务执行成功
        return true;
    }

    //主线程
    public static void main(String[] args) throws Exception
    {
        //创建三个线程对象
        TestCallable t1 = new TestCallable(
                "https://img-blog.csdnimg.cn/direct/beb94daf07094fd5979693243c8ac60b.png",
                "1.jpg");
        TestCallable t2 = new TestCallable(
                "https://img-blog.csdnimg.cn/direct/beb94daf07094fd5979693243c8ac60b.png",
                "2.jpg");
        TestCallable t3 = new TestCallable(
                "https://img-blog.csdnimg.cn/direct/beb94daf07094fd5979693243c8ac60b.png",
                "3.jpg");

        //ExecutorService是Java中用于管理线程池的接口,通过ExecutorService,可以创建一个线程池,并使用该线程池执行任务

        // 1、创建执行服务:创建了一个固定大小为3的线程池,这意味着该线程池最多同时运行3个线程    fixed:固定的
        ExecutorService ser = Executors.newFixedThreadPool(3);

        // 2、提交执行:使用ExecutorService的submit()方法提交了三个任务(t1、t2、t3)给线程池执行,并且通过Future对象(r1、r2、r3)接收返回结果
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);

        // 3、获取返回值结果
        Boolean result1 = r1.get();
        Boolean result2 = r2.get();
        Boolean result3 = r3.get();

        System.err.println(result1);
        System.err.println(result2);
        System.err.println(result3);

        // 4、关闭服务,也就是关闭线程池
        ser.shutdown();

    }

    //图片下载器
    class WebDownloader
    {
        //下载方法
        public void downloader(String url, String name)
        {
            try
            {
                //把网络图片地址url变成一个文件,参数1:网络图片地址 ;参数2:下载后保存的文件名
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e)
            {
                e.printStackTrace();
                System.err.println("IO异常,downloader 方法出现问题");
            }
        }
    }
}

4、总结:三种创建方式的区别:

Thread、Runnable、Callable,都能用来开启多个线程来执行业务逻辑。

继承Thread 类 和 实现 Runnable接口的对比:

OOP 指的是面向对象编程(Object-Oriented Programming)
在这里插入图片描述


继承 Thread 类:(重写run()方法)
自定义线程子类A,继承 Thread 类,然后重写 run() 方法,把具体的业务逻辑写在run方法里面。
通过子类自身去调用 start() 方法,让 JVM 去开启一条新的线程去执行 run() 方法。

这样就可以启动多条线程。


实现 Runnable 接口:(实现 run() 方法)
自定义线程类B,实现 Runnable 接口,它启动多个线程,是通过 创建多个Thread 线程对象,然后把线程类B作为参数传给 Thread 线程对象,由Thread线程对象来调用 start 方法的


实现 Callable 接口:(实现 call() 方法)

自定义线程类C,实现 Callable 接口,它启动多个线程,是通过创建一个线程池,然后把线程类对象C作为任务参数提交到线程池,由线程池负责提供新线程来执行线程类对象C中定义的任务逻辑
线程类对象C中定义的任务逻辑,就是指重写 call() 方法里面的业务逻辑。


在这里插入图片描述



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

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

相关文章

干货分享 | 在TSMaster中加载基于DotNet平台的SeedKey

在UDS诊断过程中&#xff0c;会涉及到安全访问的问题&#xff0c;也就是所谓的Seed&Key。TSMaster 诊断模块支持通过.dll文件载入 Seed&Key 算法用于安全访问解锁。在最近发布的TSMaster 2024.03版本中不仅支持了C/C&#xff0c;Delphi等语言封装的DLL文件&#xff0c;…

基于GD32的简易数字示波器(3)- PCB设计

这期记录的是项目实战&#xff0c;做一个简易的数字示波器。 教程来源于嘉立创&#xff0c; 本期介绍PCB设计的大致流程。 下图为示波器的指标 具有选择交流耦合还是直流耦合功能、输入信号不衰减或衰减50倍 输入频率理论最大800KHz输入幅值&#xff08;不衰减&#xff09;…

【RISC-V 指令集】RISC-V 向量V扩展指令集介绍(四)- 配置和设置指令(vsetvli/vsetivli/vsetvl)

1. 引言 以下是《riscv-v-spec-1.0.pdf》文档的关键内容&#xff1a; 这是一份关于向量扩展的详细技术文档&#xff0c;内容覆盖了向量指令集的多个关键方面&#xff0c;如向量寄存器状态映射、向量指令格式、向量加载和存储操作、向量内存对齐约束、向量内存一致性模型、向量…

SUSE Linux Enterprise Server安装

1. SUSE镜像下载 下载地址&#xff1a;Evaluation Copy of SUSE Linux Enterprise Server | SUSE 选择自己需要的版本和对应的架构 选择下载SLE-15-SP5-Full-x86_64-GM-Media1.iso&#xff0c;下载时需要注册请按照提示进行注册。 2. 安装SUSE Linux 安装时可以通过连接服务…

MySQL高级(索引结构Hash,为什么InnoDB存储引擎选择使用B+tree索引结构?)

目录 1、Hash索引结构 2、Hash索引特点 3、存储引擎支持 4、为什么InnoDB存储引擎选择使用Btree索引结构&#xff1f; 1、Hash索引结构 哈希索引就是采用一定的hash算法&#xff0c;将键值换算成新的hash值&#xff0c;映射到对应的槽位上&#xff0c;然后存储在hash表中。 如…

希尔排序解读

在算法世界中&#xff0c;排序算法是至关重要的一部分。而希尔排序&#xff08;Shell Sort&#xff09;作为一种基于插入排序的改进算法&#xff0c;通过允许交换非相邻元素&#xff0c;从而在一定程度上提高了排序效率。本文将深入探讨希尔排序的原理、实现方式以及它的性能特…

电商技术揭秘十五:数据挖掘与用户行为分析

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台…

【3GPP】【核心网】核心网/蜂窝网络重点知识面试题一(超详细)

目录 1. 核心网技术演进、各大运营商采用的技术是什么&#xff1f; 2. 整体掌握lte网络架构&#xff0c;能画出网络拓扑&#xff0c;并指出关键接口的位置 3. 对于主要的LTE核心网网元&#xff0c;讲讲自己对其功能的理解 4. 核心网常用字段概念&#xff1a;imsi、msisdn、…

Python学习从0到1 day21 第二阶段 面向对象 ④ 类型注解

仗剑红尘已是癫&#xff0c;有酒平步上青天 —— 24.4.7 一、变量的类型注解 学习目标 1.理解为什么使用类型注解 2.掌握变量的类型注解语法 为什么使用类型注解 tip&#xff1a;CTRLP&#xff0c;可以提示函数中传入的参数 当我们需要使用pycharm的自动补全功能&#xff0c;又…

论如何在小程序展示超链接在线网页

在工作中遇到一个需求&#xff0c;就是在小程序中展示超链接网页&#xff0c;起初我是直接使用web-view标签 <web-view src"https://www.baidu.com/"/>但是web-view只能在开发阶段手机上展示&#xff0c;一旦小程序发布线上&#xff0c;就会出现下面这种情况“…

Unity类银河恶魔城学习记录12-7-2 p129 Craft UI - part 2源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI_CraftWindow.cs using UnityEngine.UI; using TMPro; using UnityEngin…

如何通过跨网软件,实现网络隔离后的文件安全收发摆渡?

随着企业数字化转型的逐步深入&#xff0c;企业投入了大量资源进行信息系统建设&#xff0c;信息化程度日益提升。绝大多数企业为了防止内部核心数据泄露&#xff0c;会实施网络隔离&#xff0c;比如内外网隔离&#xff0c;或者在内部网络中又划分出研发网、办公网、生产网等。…

三、SpringBoot3 整合 SpringMVC

本章概要 实现过程web 相关配置静态资源处理自定义拦截器(SpringMVC 配置) 3.1 实现过程 创建程序引入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www…

【数据结构与算法】力扣 142. 环形链表 II

题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统…

安卓远离手机app

软件介绍 远离手机是专门为防止年轻人上瘾而打造的生活管理类的软件,适度用手机&#xff0c;保护眼睛&#xff0c;节约时间。 下载 安卓远离手机app

Codeforces CROC 2016 - Final Round B. Graph Coloring【2-SAT、二分图染色】

B. Graph Coloring 题意 有 n n n 个节点和 m m m 条边&#xff0c;起初每条边都有具有颜色 0 0 0 或 1 1 1 其中一种&#xff0c;可以选择一个节点&#xff0c;并将所有与这个点直接相连的边的颜色都翻转&#xff0c;问最少需要选择多少节点才能使所有边的颜色都一样&…

Pycharm2024安装

Pycharm2024安装 1.解压文件 获取方式在最后&#xff01;&#xff01;&#xff01;安装方式一样&#xff01; 2.双击安装 2.1.点击下一步 2.2.根据情况选择安装路径 最好不要使用中文。 2.3.勾选安装配置项 只选择创建桌面快捷方式就行了&#xff0c;其他选项可以不用。…

第一部分 Vue讲解(代码版)

1.第一个vue实例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-w…

【Java面试题】MySQL上篇(索引)

文章目录 索引1.索引的分类&#xff1f;2.B树和B树的区别&#xff1f;2.1B树2.2B树 3.为什么使用索引会加快查询&#xff1f;4.创建索引的注意点&#xff1f;5.索引在哪些情况下会失效&#xff1f;6.聚簇索引和非聚簇索引的区别&#xff1f;7.回表查询是什么&#xff1f;8.什么…

思迈特:“人工智能+”浪潮里,国产BI到了关键时刻

作为首个“AI程序员”&#xff0c;Devin最近参与了一系列工作&#xff0c;包括在人力资源外包平台Upwork完成编程工作&#xff1b;潜入一家明星创业公司内部群交流&#xff0c;为公司CTO调整代码方案等。这让整个软件工程行业大受震撼&#xff0c;程序员留言“刷屏”。 “AI…