简单了解java中线程的使用

线程

1、线程的相关概念

1.1、并行和并发

并行:在同一时刻,有多个任务在多个CPU上同时执行

并发:在同一时刻,有多个任务在单个CPU上交替执行

1.2、进程和线程

进程:就是在多任务管理系统中,每个独立执行的程序,进程就是”正在进行的程序“

线程:就是程序运行的基本单元。当操作系统执行一个程序时,会在系统中建立一个进程,该进程必须至少建立一个线程(这个线程被称为主线程)作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个线程

2、什么是多线程?

是指从软件或者硬件上实现多个线程并发执行的技术。

具有多线程能力的计算机因为硬件支持而能够在同一时间执行多个线程,提升性能。

3、线程的使用

3.1、使用多线程的好处

提高程序的效率

3.2、多线程如何开发

3.2.1、线程启动方式之Thread类(java语言提供的线程类)

概述:java.lang.Thread是线程类,可以用来给进程创建线程处理任务使用,其中线程中有两个中要的方法:

  • public void run():线程执行任务的方法,是线程启动后第一个执行的方法
  • public void start():启动线程的方法,线程对象调用该方法后,Java虚拟机就会调用此线程的run方法

线程启动步骤:

  • 创建一个子类继承Thread类(创建的子类也是线程类)
  • 在子类中,编写让线程完成的任务(任务代码)
  • 重写Thread类中的run方法(线程任务)
  • 启动线程

示例:

创建一个子类线程:

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("我的线程开始啦");
        for (int i = 100; i < 200; i++) {
            System.out.println("我的线程:" + i);
        }
    }
}

在main中启动子线程

public class ThreadDemo1 {
    public static void main(String[] args) {
        //创建一个线程对象
        MyThread myThread = new MyThread();

        //启动线程
        myThread.start();

        //主线程代码
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程:" + i);
        }
    }
}

结果展示(展示部分):

主线程:0
我的线程开始啦
主线程:1
我的线程:100
我的线程:101
我的线程:102
主线程:2
我的线程:103
主线程:3
我的线程:104
我的线程:105
我的线程:106
主线程:4
主线程:5
我的线程:107
主线程:6
我的线程:108
主线程:7
主线程:8
主线程:9
主线程:10
主线程:11
主线程:12
主线程:13
主线程:14
我的线程:109
主线程:15
我的线程:110
我的线程:111
我的线程:112
我的线程:113
我的线程:114
我的线程:115
主线程:25
...
3.2.2、线程启动方式之Runnable接口(推荐使用,灵活度高,因为允许子类继承其他父类)

使用的构造方法:

  • public Thread(Runnable target)

  • public Thread(Runnable target, String name)

参数中的Runnable是一个接口,用来定义线程要执行的任务

线程启动步骤:

  • 定义任务类实现Runnable,并重写run方法
  • 创建任务对象
  • 使用含有Runnable参数的构造方法,创建线程对象并指定任务
  • 调用线程start方法,开启线程

示例:

任务类

public class MyTask implements Runnable{
    @Override
    public void run() {
        for (int i = 100; i < 200; i++) {
            System.out.println("新线程:"+ i);
        }
    }
}

启动线程

public class ThreadDemo1 {
    public static void main(String[] args) {
        MyTask myTask = new MyTask();

        Thread t = new Thread(myTask);

        t.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("主线程:" + i);
        }
    }
}

运行结果与上面相似

4、线程中的常用方法

方法说明
String getName()获取线程名字
viod setName()给线程设置一个名字
public static Thread currentThread()返回当前正在执行的线程对象的引用
public static void sleep(long time)让线程休眠指定的时间,单位为毫秒
public void join()具备阻塞作用,等待这个线程死亡,才会执行其他线程

获取线程名字示例:

public class ThreadTask implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            //获取                     当前线程          的名字
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}


public class Test1 {
    public static void main(String[] args) {
        new Thread(new ThreadTask()).start();

        for (int i = 100; i <120 ; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

运行结果(部分展示):

main: 100
Thread-0: 0
main: 101
Thread-0: 1
main: 102
Thread-0: 2
main: 103
Thread-0: 3
main: 104
Thread-0: 4
main: 105

其他方法,大家可以自行尝试。

5、线程的安全问题

5.1、发生安全问题的原因

多个线程对同一个数据,进行读写操作,造成数据错乱

案例分析:我们现在设计一个卖票程序,票数总共100张,设置三个售票处

思路:

  • 定义一个类Ticket实现Runnable接口,里面定义一个成员变量Private int count = 100
  • 在Ticket类中重写run()方法实现卖票,代码如下

Ticket类

public class Ticket implements Runnable{
    private int count = 100;

    @Override
    public void run() {
        while (true){
            if(count > 0){

                //模拟出票时间延迟
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                System.out.println(Thread.currentThread().getName() + "票号:" + count);
                count--;
            }
            if(count == 0){
                break;
            }
        }
    }
}

Test类

public class Test {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        //三个售票处
        Thread t1 = new Thread(ticket, "售票处1-");
        Thread t2 = new Thread(ticket, "售票处2-");
        Thread t3 = new Thread(ticket, "售票处3-");

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

当我们运行后,发现结果出现了问题,有些情况不在我们的预期之中,不符合常理

比如出现了重复票:

售票异常1

还有出现了0号票:

售票异常2

为什么会出现这些奇怪的问题呢?很简单,这是因为多线程操作共享数据。解决这些问题的基本思路就是让共享数据存在安全环境中,当某一个线程访问共享数据时,其他线程是无法操作的

具体实现:

  • 把多条线程操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
  • java提供了同步代码块的实现方式来解决

5.2、同步代码块

5.2.1、线程同步

java允许多线程并发执行,当多个线程同时操作一个可共享的资源变量时(比如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁,以避免在该线程没有完成操作之前,被其它线程调用,从而保证该变量的唯一性和准确性

5.2.2、同步代码块

锁住多条语句操作共享数据,可以使用同步代码块实现

  • 格式:
synchronized(任意对象){
    多条件语句操作共享数据的代码
}
  • 默认情况锁是打开的,只要有一个线程进去执行代码了,锁就会关闭
  • 当线程执行完成出来后,锁才会自动打开
  • 锁对象是任意对象,但是多个线程必须使用同一把锁

同步的好处和弊端:

  • 好处:解决了多线程的数据安全问题
  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

所以上面Ticket类中可以修改为:

public class Ticket implements Runnable{
    private int count = 100;

    Object lock = new Object();

    @Override
    public void run() {
        while (true){
            synchronized (lock){//锁对象可以时任意对象
                if(count > 0){

                    //模拟出票时间延迟
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    System.out.println(Thread.currentThread().getName() + "票号:" + count);
                    count--;
                }
                if(count <= 0){
                    break;
                }
            }
        }
    }
}
5.2.3、同步方法

**概念:**就是把synchronized关键字加到方法上,保证线程执行该方法的时候,其他线程只能在方法外等着

  • 格式
修饰符 synchronized 返回值类型 方法名(方法参数){

}
  • 同步代码块和同步方法的区别

    同步代码块可以锁住指定代码,同步方法是锁住方法中的所有代码

    同步代码块可以指定锁对象,同步方法不能指定锁对象

虽然同步方法不能指定锁对象,但是有默认存在的锁对象

对于非static方法,同步锁就是this

对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。Class类型的对象

public class Ticket implements Runnable{
    private int count = 100;

    @Override
    public void run() {
        while (true){
            if(count <= 0){
                break;
            }

            demo();
        }
    }

    private synchronized void demo(){
        if(count > 0){

            //模拟出票时间延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println(Thread.currentThread().getName() + "票号:" + count);
            count--;
        }
    }
}

6、Lock锁机制

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了它,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。

Lock中提供了获得锁和释放锁的方法

  • void lock():获得锁
  • void unlock():释放锁

Lock是接口不能实例化,这里采用它的实现类ReentrantLock来实例化,ReentrantLock的构造方法是:ReentrantLock()。

注意:多个线程使用相同的Lock锁对象,需要多线程操作数据的代码放在lock()和unlock()方法之间。一定要确保最后unlock能够调用

上述Ticket类可以修改为这样:

public class Ticket implements Runnable{
    private int count = 100;

    //获取锁对象
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            //上锁
            lock.lock();
            if(count <= 0){
                break;
            }

            if(count > 0){

                //模拟出票时间延迟
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                System.out.println(Thread.currentThread().getName() + "票号:" + count);
                count--;
            }
            //释放锁
            lock.unlock();
        }
    }
}

7、线程通讯

7.1、概念

在多线程程序中,某个线程进入到等待状态时,必须有其他线程来唤醒处于等待状态的线程。

7.2、线程通讯需要使用的API

7.2.1、等待方法
  • wait():无线等待(只能由其他线程唤醒)
  • wait(long 毫秒):计时等待,时间到了自动唤醒

以上两个方法调用会导致当前线程释放掉锁资源

7.2.3、唤醒方法
  • notify():唤醒处于等待状态的任意个线程
  • notityAll():唤醒处于等待状态的所有线程

以上两个方法调用不会导致当前线程释放掉锁资源

无限等待唤醒示例:

public class Test {
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            Object lock = new Object();
            boolean flag = true;

            @Override
            public void run() {
                synchronized (lock){
                    if(flag){
                        flag = false;
                        System.out.println("线程进入无限等待...");
                        try {
                            lock.wait();
                            System.out.println("程序继续执行啦~");
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else{
                        System.out.println("线程即将被唤醒");
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        lock.notify();
                    }
                }
            }
        };

        new Thread(task).start();
        new Thread(task).start();
    }
}

计时等待唤醒示例:

public class Test01 {
    public static void main(String[] args) {
        new Thread(() -> {
            Object lock = new Object();

           synchronized (lock){
               try {
                   System.out.println("即将进入计时等待...");
                   lock.wait(2000);
                   System.out.println("两秒了,程序继续执行");
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        }).start();
    }
}

总结:

  • 等待和唤醒方法调用需要使用对象锁,需要在同步代码中完成
  • wait()进入无限等待,wait(时间)进入计时等待
  • 唤醒线程的方法是notify【唤醒处于和notify使用同一对象锁上的,处于等待状态的任意线程】、notifyAll【同notify】

最后给大家来一个经典的生产者消费者简单线程代码示例:

公共资源类

public class Food {
    //判断是否有食物
    public static boolean food = true;

    //锁
    public static final Object lock = new Object();
}

生产者

public class Producer implements Runnable{
    @Override
    public void run() {
        synchronized (Food.lock){
            while(true){
                //判断是否有食物,有食物则进行等待,反之则生产食物
                if(Food.food){
                    //将食物标识设置为无
                    //Food.food = false;

                    System.out.println(Thread.currentThread().getName() + "此时进行等待消费者消费~");

                    try {
                        Food.lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }else{
                    System.out.println(Thread.currentThread().getName() + "发现没有食物,开始生产~");

                    //制作食物的时间
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    Food.food = true;

                    //唤醒消费者
                    Food.lock.notify();
                }
            }
        }
    }

    public Producer() {
    }
}

消费者

public class Consumer implements Runnable{
    @Override
    public void run() {
        //判断是否有食物,有则消费,没有则等待
        synchronized (Food.lock){
            while(true){
                if(Food.food){
                    System.out.println(Thread.currentThread().getName() + "发现食物,开炫~");

                    //消费时长
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    Food.food = false;

                    //唤醒生产者继续生产
                    Food.lock.notify();
                }else{
                    System.out.println(Thread.currentThread().getName() + "没有发现食物,叫厨师~");

                    try {
                        Food.lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    public Consumer() {
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        new Thread(new Producer(), "生产者").start();
        new Thread(new Consumer(), "消费者").start();
    }
}

部分运行结果

生产者此时进行等待消费者消费~
消费者发现食物,开炫~
消费者没有发现食物,叫厨师~
生产者发现没有食物,开始生产~
生产者此时进行等待消费者消费~
消费者发现食物,开炫~
消费者没有发现食物,叫厨师~
......

8、线程池

8.1、概述

8.1.1、线程使用存在的问题

如果并发的线程数量很多,并且每个线程都是执行一个很短的任务就结束了,这样频繁的创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

如果大量线程在执行,会涉及到线程间上下文的切换,会极大的消耗CPU运算资源

8.1.2、线程池认识

其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源

8.1.3、线程池使用的大致流程
  • 创建线程池,指定线程开启数量
  • 提交任务给线程池,线程池中的线程就会获取任务,进行任务处理
  • 线程处理完任务不会销毁,而是返回到线程池中,等待下一个任务执行
  • 如果线程池中所有的线程都被占用,提交任务,只能等待线程池中的线程处理完当前任务
8.1.4、线程池的好处
  • 降低资源消耗:减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可以执行多个任务。
  • 提高响应速度:当任务到达时,任务可以不需要等待线程创建,就能立即执行
  • 提高线程的可管理性:可以根据系统的承受能力,调整线程池中工作线线程池的数目,防止因为消耗过多内存,服务器死机。

8.2、线程池中的API

8.2.1、线程池API学习

java.util.concurrent.ExecutorService是线程池接口类型,使用时我们不需要自己实现,JDK已经帮我们实现好了。获取线程池我们使用工具类java.util.concurrent.Executor的静态方法。

public static ExecutorService newFixedThreadPool(int num)指定线程池最大线程池数量获取线程池

线程池ExcutorService的相关方法:

提交执行的任务方法

< T>Future< T>submit(Callable< T> task)

Future< ?> submit(Runnable task)

关闭线程池方法

void shutdown() 启动一次顺序关闭,执行以前提交任务,但不接受新任务

示例:有三个老师要对五个学生进行一对一辅导:

public class Pool implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "在辅导学生学习");
    }
}

public class Test {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(3);

        pool.submit(new Pool());
        pool.submit(new Pool());
        pool.submit(new Pool());
        pool.submit(new Pool());
        pool.submit(new Pool());
    }
}

测试结果:

pool-1-thread-2在辅导学生学习
pool-1-thread-1在辅导学生学习
pool-1-thread-3在辅导学生学习
pool-1-thread-1在辅导学生学习
pool-1-thread-2在辅导学生学习

8.3、Callable接口

8.3.1、概述
public interface Callable<V> {
	V call() throws Exception;
}

Callable和Runnable的不同点:

  • Callable支持返回结果,Runnable不行
  • Callable支持抛出异常,Runnable不行
8.3.2、使用步骤
  • 创建线程池
  • 定义Callable任务
  • 创建Callable任务,提交任务给线程池
  • 获取执行结果

利用线程池计算0-n的和并返回结果:

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService demo = Executors.newFixedThreadPool(10);

        Callable<Integer> task = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 10; i++) {
                    sum += i;
                }
                return sum;
            }
        };

        Future<Integer> future = demo.submit(task);
        System.out.println(future.get());
    }
}

ble和Runnable的不同点:**

  • Callable支持返回结果,Runnable不行
  • Callable支持抛出异常,Runnable不行
8.3.2、使用步骤
  • 创建线程池
  • 定义Callable任务
  • 创建Callable任务,提交任务给线程池
  • 获取执行结果

利用线程池计算0-n的和并返回结果:~

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService demo = Executors.newFixedThreadPool(10);

        Callable<Integer> task = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 10; i++) {
                    sum += i;
                }
                return sum;
            }
        };

        Future<Integer> future = demo.submit(task);
        System.out.println(future.get());
    }
}

呼~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1502 - JUC高并发

慢慢挣&#xff0c;今天比昨天更有钱&#xff0c;明天比今天有钱&#xff0c;后天比明天有钱。 0.思维导图 6.多线程锁 synchronized实现同步的基础&#xff1a;Java中的每一个对象都可以作为锁。 具体表现为以下3中形式 对于普通同步方法&#xff0c;锁是当前实例对象。对于…

go语音进阶 多任务

多任务 什么叫 多任务&#xff1f;简单说&#xff1a;就像是操作系统可以同时执行 多个任务。打个比方 你一边使用 浏览器上网&#xff0c;一遍在听MP3, 一边再用 word 赶作业。对于电脑来讲这就是多任务&#xff0c;还有很多任务悄悄的在后台同时运行着&#xff0c;只是桌面上…

【Ardiuno】实验使用ESP32单片机连接Wifi(图文)

ESP32单片机最为精华和有特色的地方当然是wifi连接&#xff0c;这里我们就写程序实验一下适使用ESP32主板连接wifi&#xff0c;为了简化实验我们这里只做了连接部分&#xff0c;其他实验在后续再继续。 由于本实验只要在串口监视器中查看结果状态即可&#xff0c;因此电路板上…

macOS 15 beta (24A5264n) Boot ISO 原版可引导镜像下载

macOS 15 beta (24A5264n) Boot ISO 原版可引导镜像下载 iPhone 镜像、Safari 浏览器重大更新、备受瞩目的游戏和 Apple Intelligence 等众多全新功能令 Mac 使用体验再升级 请访问原文链接&#xff1a;https://sysin.org/blog/macOS-Sequoia-boot-iso/&#xff0c;查看最新版…

如何有效释放Docker占用的存储空间

随着Docker的广泛应用&#xff0c;我们经常会遇到Docker占用过多存储空间的问题。这可能是由于频繁的镜像拉取、容器创建和删除等操作导致的。本文将介绍几种方法来有效释放Docker占用的存储空间&#xff0c;特别是docker system prune命令的使用。 Docker的存储机制 Docker使…

springboot连接多个库

一个SpringBoot项目&#xff0c;同时连接两个数据库&#xff1a;比如一个是Mysql数据库&#xff0c;一个是oracle数据库&#xff08;啥数据库都一样&#xff0c;连接两个同为oracle的数据库&#xff0c;或两个不同的数据库&#xff0c;只需要更改对应的driver-class-name和jdbc…

【C++】list 容器的增删改查---模拟实现(图例超详细解析!!!)

目录 一、前言 二、 list 容器的模拟实现思 ✨ 模块分析 ✨ 作用分析 三、list的节点类设计 四、list 的迭代器类设计 ⭐ 迭代器类--存在的意义 ⭐ 迭代器类--模拟实现 &#x1f4a6; 模板参数 和 成员变量 &#x1f4a6; 构造函数 &#x1f4a6; 运算符的重载 &…

HyperBDR新版本上线,自动化容灾兼容再升级!

本次HyperBDR v5.5.0版本新增完成HCS&#xff08;Huawei Cloud Stack&#xff09;8.3.x和HCSO&#xff08;Huawei Cloud Stack Online&#xff09;自动化对接&#xff0c;另外还突破性完成了Oracle云(块存储模式)的自动化对接。 HyperBDR&#xff0c;云原生业务级别容灾工具。支…

PS教程系统17

橡皮擦工具 主要配合画笔工具来使用 选择画笔工具新建图层试验擦除线条 如果直接在背景图片上进行擦除 会有背景颜色补充 背景橡皮擦 将其白色背景擦除掉shift相关键&#xff0c;进行工作区域切换吸取样点一次采样、两次采样连续、不连续等功能 在进行涂擦的过程一…

Unity EasyRoads3D插件使用

一、插件介绍 描述 Unity 中的道路基础设施和参数化建模 在 Unity 中使用内置的可自定义动态交叉预制件和基于您自己导入的模型的自定义交叉预制件&#xff0c;直接创建独特的道路网络。 添加额外辅助对象&#xff0c;让你的场景栩栩如生&#xff1a;桥梁、安全护栏、栅栏、墙壁…

RawChatGPT:公益大模型使用网站

文章目录 一、Rawchat介绍二、使用教程三、案例应用3.1 图片内容分析3.2 生图演示3.3 文档解析 一、Rawchat介绍 RawChat为用户提供了更为便捷的使用方式。 二、使用教程 RawChat公益站点链接&#xff1a;https://ChatGPTplus.cn 进入后&#xff0c;我们只需要点击&#xf…

基于Java+Swing+mysql幼儿园信息管理系统V2

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

深入理解rtmp(三)之手把手实现握手协议

RTMP是基于TCP协议的应用层协议,默认通信端口1935.实现握手协议前先了解一下rtmp握手协议吧!!! 握手过程 要建立一个有效的RTMP Connection链接&#xff0c;首先要“握手”:客户端要向服务器发送C0,C1,C2&#xff08;按序&#xff09;三个chunk&#xff0c;服务器向客户端发送…

Linux1(介绍与基本命令)

目录 一、初始Linux 1. Linux的起源 2. Linux是什么&#xff1f; 3. Linux内核版本 4. Linux的应用 5. 终端 6. Shell 7. Linux目录结构 二、基本命令 1. 基本的命令格式 2. shutdown 关机命令 3. pwd 当前工作目录 4. ls 查看目录内容 5. cd 改变工作目录 …

揭秘速卖通API接口:打破电商边界,用代码驱动全球业务增长

速卖通&#xff08;AliExpress&#xff09;通常指的是阿里巴巴集团旗下的国际零售电商平台。然而&#xff0c;直接通过API接口与速卖通进行交互通常涉及阿里巴巴的开放平台&#xff08;Open Platform&#xff09;和相关API。由于API的具体细节、认证方式、请求参数和返回值可能…

六种图算法的python实现

六种图算法的python实现 1. Prim 算法 基本原理 Prim算法是一种求解最小生成树的贪心算法。所谓最小生成树&#xff0c;就是对于给定的连通图&#xff0c;找到一棵包含所有顶点的树&#xff0c;且树上所有边的权重之和最小。Prim算法从一个顶点开始&#xff0c;每次选择与当…

数据丢失?揭秘easyrecovery破解版下载安装步骤教程,一键恢复!

“我不小心把硬盘里的重要文件删了&#xff0c;怎么都找不到了&#xff01;” “电脑突然崩溃了&#xff0c;所有的数据都没了&#xff0c;怎么办&#xff1f;” 这些情况是不是让你感到绝望&#xff1f;不过别担心&#xff0c;EasyRecovery数据恢复软件可以帮你轻松解决这些问…

[office] excel表格中双击鼠标左键有什么快捷作用- #经验分享#媒体

excel表格中双击鼠标左键有什么快捷作用? excel表格中双击鼠标左键有什么快捷作用&#xff1f;不要小看鼠标左键双击的作用&#xff0c;在excel中双击鼠标左键可以实现六个功能&#xff0c;提高工作效率&#xff0c;到底是那六个功能呢&#xff1f;请看下文详细介绍 在表格中…

R语言绘图 --- 桑基图(Biorplot 开发日志 --- 5)

「写在前面」 在科研数据分析中我们会重复地绘制一些图形&#xff0c;如果代码管理不当经常就会忘记之前绘图的代码。于是我计划开发一个 R 包&#xff08;Biorplot&#xff09;&#xff0c;用来管理自己 R 语言绘图的代码。本系列文章用于记录 Biorplot 包开发日志。 相关链接…

React基础教程:TodoList案例

todoList案例——增加 定义状态 // 定义状态state {list: ["kevin", "book", "paul"]}利用ul遍历list数组 <ul>{this.state.list.map(item ><li style{{fontWeight: "bold", fontSize: "20px"}} key{item.i…