day12 多线程

news2024/11/14 20:40:42

目录

1.概念相关

1.1什么是线程

1.2什么是多线程

2.创建线程

2.1方式一:继承Thread类

2.1.1实现步骤

2.1.2优缺点

2.1.3注意事项

2.2方式二:实现Runnable接口

2.2.1实现步骤

2.2.2优缺点

2.2.3匿名内部类写法

2.3方式三:实现callable接口

2.3.1callable接口解决了什么问题

2.3.2实现步骤

2.3.3FutureTask的API

2.3.4优缺点

3.线程的常用方法

4.线程安全

4.1什么是线程安全

4.2线程安全问题出现的原因

4.3模拟线程安全问题场景 :取钱

5.线程同步

5.1什么是线程同步

5.2线程同步的核心思想

5.3常见方案

5.4方式一:同步代码块

5.4.1作用:

5.4.2原理:

5.4.3注意事项:

5.4.4锁对象的使用规范 

5.4.5如何实现线程安全的

5.5方式二:同步方法

5.5.1作用

5.5.2原理

5.5.3底层原理

5.5.4同步代码块和同步方法哪种好

5.6lock锁

5.6.1lock锁是什么

5.6.2lock锁的构造器、常用方法

5.6.3锁对象建议加上什么修饰

5.6.4释放锁的操作建议放到哪里

6.线程池

6.1认识线程池

6.2不使用线程池的后果

6.3创建线程池

6.4任务拒绝策略

6.5线程池的注意事项

6.6处理runnable任务

6.4.1ExecutorService的常用方法

6.7处理callable

6.8通过Executors创建线程池

6.8.1方法

6.8.2Executors使用可能存在的陷阱

7.并发、并行

7.1进程

7.2并发的含义

7.3并行的含义


 

1.概念相关

1.1什么是线程

线程(Thread)是一个程序内部的一条执行流程。程序中如果只有一条执行流程,那这个程序就是单线程的程序。

1.2什么是多线程

多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)。

2.创建线程

2.1方式一:继承Thread类

2.1.1实现步骤

【1】定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法

【2】创建MyThread类的对象

【3】调用线程对象的start()方法启动线程(启动后还是执行run方法的)

2.1.2优缺点

【1】优点:编码简单

【2】缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展

2.1.3注意事项

【1】启动线程必须是调用start方法,不是调用run方法。

【2】不要把主线程任务放在启动子线程之前。

public class Test {
    public static void main(String[] args) {
        Thread t1=new MyThread();
        t1.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程"+i);
        }
    }

}

class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++){
            System.out.println("子线程" + i);
        }
    }
}

2.2方式二:实现Runnable接口

2.2.1实现步骤

【1】定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法

【2】创建MyRunnable任务对象

【3】把MyRunnable任务对象交给Thread处理

【4】调用线程对象的start()方法启动线程

2.2.2优缺点

【1】优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。

【2】缺点:需要多一个Runnable对象。

2.2.3匿名内部类写法

【1】可以创建Runnable的匿名内部类对象。

【2】再交给Thread线程对象。

【3】再调用线程对象的start()启动线程。

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程"+i);
                }
            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程2"+i);
            }
        }).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程"+i);
        }
    }

2.3方式三:实现callable接口

2.3.1callable接口解决了什么问题

假如线程执行完毕后有一些数据需要返回,前两种重写的run方法均不能直接返回结果。使用Callable接口和FutureTask类来实现创建,可以返回线程执行完毕后的结果。

2.3.2实现步骤

【1】定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据。

【2】把Callable类型的对象封装成FutureTask(线程任务对象)。

【3】把线程任务对象交给Thread对象。

【4】调用Thread对象的start方法启动线程。

【5】线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。

2.3.3FutureTask的API

FutureTask提供的构造器说明
public FutureTask<>(Callable call)把Callable对象封装成FutureTask对象。
FutureTask提供的方法说明
public V get() throws Exception获取线程执行call方法返回的结果。

2.3.4优缺点

【1】优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。

【2】缺点:编码复杂一点。

public class ThreadDemo3 {
        public static void main(String[] args) {
        // 目标:掌握多线程的创建方式三:实现Callable接口,方式三的优势:可以获取线程执行完毕后的结果的。
        // 3、创建一个Callable接口的实现类对象。
        Callable<String> c1 = new MyCallable(100);
        // 4、把Callable对象封装成一个真正的线程任务对象FutureTask对象。
        /**
         * 未来任务对象的作用?
         *    a、本质是一个Runnable线程任务对象,可以交给Thread线程对象处理。
         *    b、可以获取线程执行完毕后的结果。
         */
        FutureTask<String> f1 = new FutureTask<>(c1); // public FutureTask(Callable<V> callable)
        // 5、把FutureTask对象作为参数传递给Thread线程对象。
        Thread t1 = new Thread(f1);
        // 6、启动线程。
        t1.start();

        Callable<String> c2 = new MyCallable(50);
        FutureTask<String> f2 = new FutureTask<>(c2); // public FutureTask(Callable<V> callable)
        Thread t2 = new Thread(f2);
        t2.start();

        // 获取线程执行完毕后返回的结果
        try {
            // 如果主线程发现第一个线程还没有执行完毕,会让出CPU,等第一个线程执行完毕后,才会往下执行!
            System.out.println(f1.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            // 如果主线程发现第二个线程还没有执行完毕,会让出CPU,等第一个线程执行完毕后,才会往下执行!
            System.out.println(f2.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// 1、定义一个实现类实现Callable接口
class MyCallable implements Callable<String> {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }
    // 2、实现call方法,定义线程执行体
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "子线程计算1-" + n + "的和是:"  + sum;
    }
}

3.线程的常用方法

Tread提供的常用方法说明
public void run()线程的任务方法
public void start()启动线程
public String getName()获取当前线程的名称,线程名称默认是Thread-索引
public void setName(String name)为线程设置名称
public static Thread currentThread()获取当前执行的线程对象
public static void sleep(long time)让当前执行的线程休眠多少毫秒后,再继续执行
public final void join()...让调用当前这个方法的线程先执行完!
public class ThreadApiDemo1 {
    public static void main(String[] args) {
        // 目标:搞清楚线程的常用方法。
        Thread t1 = new MyThread("1号线程");
        // t1.setName("1号线程");
        t1.start();
        System.out.println(t1.getName()); // 线程默认名称是:Thread-索引

        Thread t2 = new MyThread("2号线程");
        // t2.setName("2号线程");
        t2.start();
        System.out.println(t2.getName()); // 线程默认名称是:Thread-索引

        // 哪个线程调用这个代码,这个代码就拿到哪个线程
        Thread m = Thread.currentThread(); // 主线程
        m.setName("主线程");
        System.out.println(m.getName()); // main
    }
}

// 1、定义一个子类继承Thread类,成为一个线程类。
class MyThread extends Thread {
    public MyThread(String name) {
        super(name); // public Thread(String name)
    }

    // 2、重写Thread类的run方法
    @Override
    public void run() {
        // 3、在run方法中编写线程的任务代码(线程要干的活儿)
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() +"子线程输出:" + i);
        }
    }
}
public static void main(String[] args) {
        // 目标:搞清楚Thread类的Sleep方法(线程休眠)
        for (int i = 1; i <= 10; i++) {
            System.out.println(i);
            try {
                // 让当前执行的线程进入休眠状态,直到时间到了,才会继续执行。
                // 项目经理让我加上这行代码,如果用户交钱了,我就注释掉。
                Thread.sleep(1000); // 1000ms = 1s
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
public class ThreadApiDemo3 {
    public static void main(String[] args) {
        // 目标:搞清楚线程的join方法:线程插队:让调用这个方法线程先执行完毕。
        MyThread2 t1 = new MyThread2();
        t1.start();

        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() +"线程输出:" + i);
            if(i == 1){
                try {
                    t1.join(); // 插队 让t1线程先执行完毕,然后继续执行主线程
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() +"子线程输出:" + i);
        }
    }
}

4.线程安全

4.1什么是线程安全

多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。

4.2线程安全问题出现的原因

【1】存在多个线程在同时执行

【2】同时访问一个共享资源

【3】存在修改该共享资源

4.3模拟线程安全问题场景 :取钱

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
    private String cardId; // 卡号
    private double money; // 余额

    // 小明和小红都到这里来了取钱
    public synchronized void drawMoney(double money) {
        // 拿到当前谁来取钱。
        String name = Thread.currentThread().getName();
        // 判断余额是否足够
        if (this.money >= money) {
            // 余额足够,取钱
            System.out.println(name + "取钱成功,吐出了" + money + "元成功!");
            // 更新余额
            this.money -= money;
            System.out.println(name + "取钱成功,取钱后,余额剩余" + this.money + "元");

        } else {
            // 余额不足
            System.out.println(name + "取钱失败,余额不足");
        }
    }
}
// 取钱线程类
public class DrawThread extends Thread{
    private Account acc; // 记住线程对象要处理的账户对象。

    public DrawThread(String name, Account acc) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        // 小明 小红 取钱
        acc.drawMoney(100000);
    }
}
    public static void main(String[] args) {
        // 目标:模拟线程安全问题。
        // 1、设计一个账户类:用于创建小明和小红的共同账户对象,存入10万。
        Account acc = new Account("ICBC-110", 100000);

        // 2、设计线程类:创建小明和小红两个线程,模拟小明和小红同时去同一个账户取款10万。
        new DrawThread("小明", acc).start();
        new DrawThread("小红", acc).start();
    }

5.线程同步

5.1什么是线程同步

线程同步是线程安全问题的解决方案

5.2线程同步的核心思想

让多个线程先后依次访问共享资源,这样就可以避免出现线程安全问题。

5.3常见方案

加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。

5.4方式一:同步代码块

5.4.1作用:

把访问共享资源的核心代码给上锁,以此保证线程安全。

5.4.2原理:

每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行

5.4.3注意事项:

对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。

5.4.4锁对象的使用规范 

【1】建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。

【2】对于静态方法建议使用字节码(类名.class)对象作为锁对象。

5.4.5如何实现线程安全的

【1】对出现问题的核心代码使用synchronized进行加锁

【2】每次只能一个线程占锁进入访问

// 小明和小红都到这里来了取钱
    public void drawMoney(double money) {
        // 拿到当前谁来取钱。
        String name = Thread.currentThread().getName();
        // 判断余额是否足够
        synchronized (this) {
            if (this.money >= money) {
                // 余额足够,取钱
                System.out.println(name + "取钱成功,吐出了" + money + "元成功!");
                // 更新余额
                this.money -= money;
                System.out.println(name + "取钱成功,取钱后,余额剩余" + this.money + "元");
    
            } else {
                // 余额不足
                System.out.println(name + "取钱失败,余额不足");
            }
        }
    }

5.5方式二:同步方法

5.5.1作用

把访问共享资源的核心方法给上锁,以此保证线程安全。

5.5.2原理

每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行

5.5.3底层原理

【1】同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。

【2】如果方法是实例方法:同步方法默认用this作为的锁对象。

【3】如果方法是静态方法:同步方法默认用类名.class作为的锁对象。

5.5.4同步代码块和同步方法哪种好

同步代码块锁的范围更小,同步方法锁的范围更大。同步方法可读性更好

    // 小明和小红都到这里来了取钱
    public synchronized void drawMoney(double money) {
        // 拿到当前谁来取钱。
        String name = Thread.currentThread().getName();
        // 判断余额是否足够
        if (this.money >= money) {
            // 余额足够,取钱
            System.out.println(name + "取钱成功,吐出了" + money + "元成功!");
            // 更新余额
            this.money -= money;
            System.out.println(name + "取钱成功,取钱后,余额剩余" + this.money + "元");

        } else {
            // 余额不足
            System.out.println(name + "取钱失败,余额不足");
        }
    }

5.6lock锁

5.6.1lock锁是什么

Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

5.6.2lock锁的构造器、常用方法

构造器说明
public ReentrantLock​()获得Lock锁的实现类对象
方法名称说明
void lock()获得锁
void unlock()释放锁

5.6.3锁对象建议加上什么修饰

建议使用final修饰,防止被别人篡改

5.6.4释放锁的操作建议放到哪里

建议将释放锁的操作放到finally代码块中,确保锁用完了一定会被释放

    private final Lock lk = new ReentrantLock(); // 保护锁对象

    // 小明和小红都到这里来了取钱
    public void drawMoney(double money) {
        // 拿到当前谁来取钱。
        String name = Thread.currentThread().getName();
        lk.lock(); // 上锁
        try {
            // 判断余额是否足够
            if (this.money >= money) {
                // 余额足够,取钱
                System.out.println(name + "取钱成功,吐出了" + money + "元成功!");
                // 更新余额
                this.money -= money;
                System.out.println(name + "取钱成功,取钱后,余额剩余" + this.money + "元");
            } else {
                // 余额不足
                System.out.println(name + "取钱失败,余额不足");
            }
        } finally {
            lk.unlock();// 解锁
        }
    }

6.线程池

6.1认识线程池

线程池就是一个可以复用线程的技术。

6.2不使用线程池的后果

用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,  创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。

6.3创建线程池

通过ThreadPoolExecutor创建线程池。使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。

【1】参数一:corePoolSize : 指定线程池的核心线程的数量。

【2】参数二:maximumPoolSize:指定线程池的最大线程数量。

【3】参数三:keepAliveTime :指定临时线程的存活时间。

【4】参数四:unit:指定临时线程存活的时间单位(秒、分、时、天)

【5】参数五:workQueue:指定线程池的任务队列。

【6】参数六:threadFactory:指定线程池的线程工厂。

【7】参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)

6.4任务拒绝策略

策略说明
ThreadPoolExecutor.AbortPolicy()丢弃任务并抛出RejectedExecutionException异常。是默认的策略
ThreadPoolExecutor. DiscardPolicy()丢弃任务,但是不抛出异常,这是不推荐的做法
ThreadPoolExecutor. DiscardOldestPolicy()抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor. CallerRunsPolicy()由主线程负责调用任务的run()方法从而绕过线程池直接执行

6.5线程池的注意事项

// 1、定义一个线程任务类实现Runnable接口
public class MyRunnable implements Runnable {
    // 2、重写run方法,设置线程任务
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "输出:" + i);
            try {
                Thread.sleep(Integer.MAX_VALUE);
            } catch (Exception e) {
               e.printStackTrace();
            }
        }
    }
}
    public static void main(String[] args) {
        // 目标:创建线程池对象来使用。
        // 1、使用线程池的实现类ThreadPoolExecutor声明七个参数来创建线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(3, 5,
                10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
               Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

        // 2、使用线程池处理任务!看会不会复用线程?
        Runnable target = new MyRunnable();
        pool.execute(target); // 提交第1个任务 创建第1个线程 自动启动线程处理这个任务
        pool.execute(target); // 提交第2个任务 创建第2个线程 自动启动线程处理这个任务
        pool.execute(target); // 提交第2个任务 创建第3个线程 自动启动线程处理这个任务
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target); // 到了临时线程的创建时机了
        pool.execute(target); // 到了临时线程的创建时机了
        pool.execute(target); // 到了任务拒绝策略了,忙不过来

        // 3、关闭线程池 :一般不关闭线程池。
        // pool.shutdown(); // 等所有任务执行完毕后再关闭线程池!
//        pool.shutdownNow(); // 立即关闭,不管任务是否执行完毕!
    }

6.6处理runnable任务

6.4.1ExecutorService的常用方法

方法名称说明
void execute(Runnable command)执行 Runnable 任务
Future<T> submit(Callable<T> task)执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果
void shutdown()等全部任务执行完毕后,再关闭线程池!
List<Runnable> shutdownNow()立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务
        // 2、使用线程池处理任务!看会不会复用线程?
        Runnable target = new MyRunnable();
        pool.execute(target); // 提交第1个任务 创建第1个线程 自动启动线程处理这个任务
        pool.execute(target); // 提交第2个任务 创建第2个线程 自动启动线程处理这个任务
        pool.execute(target); // 提交第2个任务 创建第3个线程 自动启动线程处理这个任务
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target); // 到了临时线程的创建时机了
        pool.execute(target); // 到了临时线程的创建时机了
        pool.execute(target); // 到了任务拒绝策略了,忙不过来

        // 3、关闭线程池 :一般不关闭线程池。
        pool.shutdown(); // 等所有任务执行完毕后再关闭线程池!
        pool.shutdownNow(); // 立即关闭,不管任务是否执行完毕!

6.7处理callable

线程池如何处理Callable任务,并得到任务执行完后返回的结果?

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

6.8通过Executors创建线程池

6.8.1方法

是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象

方法名称说明
public static ExecutorService newFixedThreadPool​(int nThreads)创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。
public static ExecutorService newSingleThreadExecutor()创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。
public static ExecutorService newCachedThreadPool()线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60s则会被回收掉。
public static ScheduledExecutorService newScheduledThreadPool​(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。

6.8.2Executors使用可能存在的陷阱

【1】大型并发系统环境中使用Executors如果不注意可能会出现系统风险。

【2】不适合做大型互联网场景的线程池方案

【3】建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。

7.并发、并行

7.1进程

【1】正在运行的程序(软件)就是一个独立的进程。

【2】线程是属于进程的,一个进程中可以同时运行很多个线程。

【3】进程中的多个线程其实是并发和并行执行的。

7.2并发的含义

进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。

7.3并行的含义

在同一个时刻上,同时有多个线程在被CPU调度执行。

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

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

相关文章

鸿蒙系统开发【网络-上传和下载(ArkTS)】基本功能

网络-上传和下载&#xff08;ArkTS&#xff09; 介绍 本示例使用ohos.request接口创建上传和下载任务&#xff0c;实现上传、下载功能&#xff0c;hfs作为服务器&#xff0c;实现了文件的上传和下载和任务的查询功能。 效果预览 使用说明 1.本示例功能需要先配置服务器环境…

BootStrap前端面试常见问题

在前端面试中&#xff0c;关于Bootstrap的问题通常围绕其基本概念、使用方式、特性以及实际应用等方面展开。以下是一些常见的问题及其详细解答&#xff1a; 1. Bootstrap是哪家公司研发的&#xff1f; 回答&#xff1a;Bootstrap是由Twitter的Mark Otto和Jacob Thornton合作…

go语言day21 goland使用gin框架、gorm框架操作mysql数据库redis数据库 使用宝塔创建redis数据库

GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly. gorm package - github.com/jinzhu/gorm - Go Packages go语言day20实现投票功能项目包-CSDN博客 gin框架标准项目结构&#xff1a; models&#xff1a;存放对应实体类和gorm包增删…

Godot的节点与场景

要深入的理解节点与场景&#xff0c;我们需要跳出这两个概念来看他。说的再直白一些godot本质就是一个场景编辑器&#xff01; 场景的概念应该在我们平时看电影看电视时会经常提到&#xff0c;比如某一个打斗的场景&#xff0c;这个场景可能会被设在某一个街道&#xff0c;那么…

RIP综合练习

要求&#xff1a; 1.合理使用IP地址划分网络&#xff0c;各自创建循环接口 2.R1创建环回172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&#xff0c;R1,R2之间增加路由传递安全性 5.R5创建一个环回模拟运营商&#xff0c;不能…

Flink的DateStream API中的ProcessWindowFunction和AllWindowFunction两种用于窗口处理的函数接口的区别

目录 ProcessWindowFunction AllWindowFunction 具体区别 ProcessWindowFunction 示例 AllWindowFunction 示例 获取时间不同&#xff0c;一个数据产生的时间一个是数据处理的时间 ProcessWindowFunction AllWindowFunction 具体示例 ProcessWindowFunction 示例 Al…

CRMEB 电商系统安装及分析

CRMEB系统采用前后端分离技术&#xff0c;基于TP6Vue2.5Uniapp框架开发&#xff1b;支持微信小程序、公众号、H5、APP、PC端适配&#xff0c;数据同步&#xff01;是一套单商户新零售社交电商系统。 目录 安装 安装环境 安装过程 开始安装 安装检测 数据库配置 高级设置…

基于Cloudflare搭建私有Docker镜像源

周四原本不是发文的日子&#xff0c;主要因为两个原因&#xff1a; 第一个原因是总有人留言说 Docker 用不了&#xff0c;第二个原因是看了下上个月的阅读量&#xff0c;和之前比实在有点惨淡&#xff0c;除了文章总被人搬运外&#xff0c;我估计可能跟第一个原因多少还是有点…

计算机基础(Windows 10+Office 2016)教程 —— 第4章 计算机网络与Internet(上)

第4章 计算机网络与Internet 4.1 计算机网络概述4.1.1 计算机网络的定义4.1.2 计算机网络的发展4.1.3 计算机网络的功能4.1.4 计算机网络体系结构和TCP/IP 参考模型 4.2 计算机网络的组成和分类4.2.1 计算机网络的组成4.2.2 计算机网络的分类 4.3 网络传输介质和通信设备4.3.1 …

【Unity】3D功能开发入门系列(二)

Unity3D功能开发入门系列&#xff08;二&#xff09; 一、资源&#xff08;一&#xff09;资源文件&#xff08;二&#xff09;场景文件&#xff08;三&#xff09;资源包&#xff08;四&#xff09;Unity 资源商店&#xff08;五&#xff09;项目资源的导入 二、父子关系&…

【C语言篇】数据在内存中的存储(超详细)

文章目录 数据在内存中的存储二进制和进制转换基本概念进制转换十进制转二进制二进制转八进制和十六进制 整数在内存中的存储反码原码补码大小端字节序和字节序判断什么是大小端&#xff1f;为什么有大小端 浮点数在内存中的存储题目提出浮点数存的过程浮点数取的过程 题目解析…

Java语言程序设计——篇十一(2)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

算法导论 总结索引 | 第五部分 第二十一章:用于不相交集合的数据结构

一些应用涉及 将n个不同的元素分成一组不相交的集合。寻找包含给定元素的唯一集合 和 合并两个集合 1、不相交集合的操作 1、一个不相交集合 数据结构 维持了 一个不相交动态集的集合 S {S_1, S_2,…, S_n}。用一个代表 来标识每个集合&#xff0c;它是这个集合的某个成员。…

IoTDB 入门教程 企业篇④——安全控制 | 白名单、审计日志、登录日志和操作日志

文章目录 一、前文二、白名单2.1 配置文件iotdb-common.properties2.2 配置文件white.list2.3 注意事项 三、审计日志3.1 Cli操作日志3.2 RESTful操作日志3.3 MQTT操作日志3.4 Java操作日志3.5 C#操作日志3.6 Python操作日志 四、参考 一、前文 IoTDB入门教程——导读 IoTDB企业…

C语言9~10 DAY(合集)

数组的概念 什么是数组 数组是相同类型&#xff0c;有序数据的集合。 数组的特征 数组中的数据被称为数组的元素&#xff0c;是同构的 数组中的元素存放在内存空间里 (char player_name[6]&#xff1a;申请在内存中开辟6块连续的基于char类型的变量空间) 衍生概念&#x…

力扣高频SQL 50题(基础版)第三十七题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第三十七题176.第二高的薪水题目说明实现过程准备数据实现方式结果截图总结 力扣高频SQL 50题&#xff08;基础版&#xff09;第三十七题 176.第二高的薪水 题目说明 Employee 表&#xff1a; ----------------- …

【传知代码】基于标签相关性的多标签学习(论文复现)

在当今信息爆炸的时代&#xff0c;数据中包含的标签信息对于理解和分析复杂问题至关重要。在诸如文本分类、图像识别和推荐系统等应用中&#xff0c;如何有效地利用标签相关性提升多标签学习的效果成为了研究的热点之一。基于标签相关性的多标签学习方法&#xff0c;通过挖掘不…

存储届的奥运竞技 | 400层3D NAND最快2025到来~

随着内存巨头之间的高带宽内存 (HBM) 竞争日益激烈&#xff0c;NAND 存储器领域的竞争也在升温。据韩国媒体《etnews》报道&#xff0c;SK 海力士正在研发 400 层 NAND 闪存技术&#xff0c;计划在 2025 年底前准备好这项技术以实现量产。 报道称&#xff0c;SK 海力士目前正在…

AcWing并查集

建议先看这个 Bilibili------------------>图论——并查集(详细版) 其实M 1 2就是把1的祖先改成了2&#xff0c;然后M 3 4就是把3的祖先改成了4&#xff0c;然后查询这两数1,2的祖先是不是同一个&#xff0c;3,4的祖先是不是同一个&#xff0c;1&#xff0c;3的祖先是不是同…

【期货】收盘点评。昨天说的,p2409棕榈油在今天或者周一会走出行情

收盘点评 昨天说的&#xff0c;p2409棕榈油在今天或者周一会走出行情。事实就是如此。震荡了几天了&#xff0c;波幅不大的来回震荡&#xff0c;其实主力是不想震荡的&#xff0c;但是不震荡自己的货和行情走不出来。所以我昨天就说&#xff0c;应该就是这一两天会走出一波小行…