学习笔记072——Java中的【JUC 并发编程】

文章目录

  • JUC 并发编程
    • 1、什么是高并发
    • 2、Java 实现多线程的第三种方式
    • 3、sleep 和 wait 方法的区别
    • 4、synchronized 锁定的是谁?
    • 5、ConcurrentModificationException
    • 6、JUC 工具类
    • 7、读写锁
    • 8、线程池

JUC 并发编程

JUC 是指 Java 并发编程工具包

java.util.concurrent JDK 中的一个包,包下全部都是 Java 并发编程相关的类/接口

为什么公司如此看重并发编程的能力,并发编程的目的就是充分利用计算机的资源,把计算机的性能发挥到极致。

1、什么是高并发

并行和并发

并发是指多线程操作同一个资源,但不是同时操作,而是交替操作,单核 CPU。

并行才是真正的多个线程同时执行,多核 CPU,每个线程使用一个 CPU 的资源来运行。

我们所说的并发其实是一种程序的设计标准,在单核 CPU 的情况下,开发出来的代码应该更加充分利用资源,有效提升程序的效率。

编写出来的代码具备处理多个任务在同一时间段内执行的能力。

高并发是互联网分布式系统架构设计中必须要考虑的因素之一,它是指通过设计保证系统在互联网海量用户请求的情况下,能够确保系统正常运行。

互联网架构中,如何实现高并发设计?

垂直扩展和水平扩展

垂直扩展

提升单机的处理能力

1、增强单机的硬件性能,CPU 核数、提升内存、硬盘扩容、网卡升级。。。

2、提升软件架构性能,使用缓存来提高查询效率,使用异步请求来提升服务吞吐量,使用 NoSQL 提升数据访问能力,使用并发框架。。。
垂直扩展有上限,一定会达到某个瓶颈无法再优化。

水平扩展

水平扩展理论上没有上限,所以互联网分布式架构设计最终的解决方案还是水平扩展。

集群和分布式的区别?

集群是指每台服务器所完成的工作一样,通过增加服务器的数量来提高并发能力。

分布式是指将系统拆分成不同的模块,交给不同的服务器来完成。

饭店,一个厨师,多雇几个水平一样的厨师来应对客流量,这就是集群。

不增加厨师的数量,而是把做菜分步骤完成,洗菜,切菜,炒菜,然后给厨师雇两个助手,分别完成洗菜和切菜,厨师只负责炒菜。

2、Java 实现多线程的第三种方式

实现 Callable 接口,与 Runnable 的区别在于 Callable 方法有返回值。

在这里插入图片描述

package com.htl.test2;

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

public class Test {
    public static void main(String[] args) {
        //任务
        MyCallable myCallable = new MyCallable();
        //线程
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        new Thread(futureTask).start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("callable");
        return "success";
    }
}

Callable 和 Runnable 的区别

  • Callable 可以在任务结束后提供一个返回值,Runnable 没有这个功能。
  • Callable 中的 call 方法可以抛出异常,Runnable 的 run 不能抛异常。

3、sleep 和 wait 方法的区别

让线程暂停执行任务

来自不同的类

sleep 是 Thread 类中的方法

wait 是 Object 类中的方法

sleep 是直接让当前线程暂停执行,进入阻塞状态。暂停不是看用哪个线程对象调用,而是在哪调用;在哪调就是让当前线程休眠。

让主线程休眠

package com.htl.test;

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println("A----------------");
            }
        });

        thread.start();
        try {
            //让主线程休眠
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        for (int i = 0; i < 100; i++) {
            System.out.println("=====================B");
        }
    }
}

让子线程休眠

package com.htl.test;

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            try {
                //让子线程休眠
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 100; i++) {
                System.out.println("A----------------");
            }
        });

        thread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("=====================B");
        }
    }
}

wait 也是让线程休眠,但是不直接作用于线程对象,而是作用于线程对象访问的资源,Thread —> data,调用 data.wait,此时 Thread 就会休眠。

wait 方法有一个前提,当前线程对象必须拥有 data 对象,所以 wait 方法只能在同步方法或者同步代码块中调用,否则抛出异常。

package com.htl.test;

import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.test(i);
            }
        }).start();
    }
}

class Data {
    public synchronized void test(int i){
        if(i == 5){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i + "--------------------------");
    }
}

wait 让线程对象休眠,没有阻塞时间,如果不加处理,线程会永远阻塞下去。

如何解除阻塞?

1、指定 wait 时间,wait(long millis)

if(i == 5){
    try {
        this.wait(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

2、通过 notify 方法唤醒线程 (notify方法跟wait方法一样,必须在同步方法中调用)

package com.htl.test;

import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.test(i);
            }
        }).start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(8);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data.test2();
        }).start();
    }
}

class Data {
    //在i==5的时候让线程休眠
    public synchronized void test(int i){
        if(i == 5){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i + "--------------------------");
    }

    //唤醒线程
    public synchronized void test2(){
        this.notify();
    }
}

是否释放锁

wait 会释放锁,sleep 不会释放锁

4、synchronized 锁定的是谁?

一、如果 synchronized 修饰非静态方法,则锁定的就是方法调用者

package com.htl.test2;

import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.func1();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            data.func2();
        },"B").start();
    }
}

class Data{
    public synchronized void func1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("1...");
    }

    public synchronized void func2(){
        System.out.println("2...");
    }
}

在这里插入图片描述

synchronized 修饰的是 func1,func2,所以谁调用 func1、func2 就锁谁。

对代码进行修改:

package com.htl.test2;

import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        Data data1 = new Data();
        Data data2 = new Data();
        new Thread(()->{
            data1.func1();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            data2.func2();
        },"B").start();
    }
}

class Data{
    public synchronized void func1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("1...");
    }

    public synchronized void func2(){
        System.out.println("2...");
    }
}

在这里插入图片描述

不会排队,继续修改:

package com.htl.test2;

import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            data.func1();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            data.func3();
        },"B").start();
    }
}

class Data{
    public synchronized void func1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("1...");
    }

    public synchronized void func2(){
        System.out.println("2...");
    }

    public void func3(){
        System.out.println("3...");
    }
}

在这里插入图片描述

不会排队,因为 func3 的调用不需要锁定资源,无需等待 func3 的执行完毕即可执行。

如果方法添加了 synchronized ,不是说不能调,只是调用的是需要锁定当前对象,如果没有添加 synchronized,不需要考虑任何锁定的问题,直接调。

二、如果 synchronized 修饰静态方法,则锁定的就是,无论多少个对象调用,都会同步。

package com.htl.test2;

import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) {
        Data2 data2 = new Data2();
        Data2 data1 = new Data2();
        new Thread(()->{
            data1.func1();
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data2.func2();
        }).start();
    }
}

class Data2 {
    public synchronized static void func1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("1...");
    }

    public synchronized static void func2(){
        System.out.println("2...");
    }
}

在这里插入图片描述

三、如果 synchronized 静态方法和 synchronized 非静态方法同时存在,静态方法锁类,实例方法锁对象

package com.htl.test3;

import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        Data3 data = new Data3();
        Data3 data2 = new Data3();
        new Thread(()->{
            data.func1();
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            data2.func2();
        }).start();
    }
}

class Data3{
    public synchronized static void func1(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("1...");
    }

    public synchronized void func2(){
        System.out.println("2...");
    }
}

在这里插入图片描述

四、如果 synchronized 修饰的是代码块,则锁定的是传入的对象

能否实现同步,就看锁定的对象是否位同一个对象

package com.htl.test4;

import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        Data4 data = new Data4();
        for (int i = 0; i < 5; i++) {
            Integer num = Integer.valueOf(1);
            new Thread(()->{
                data.func(num);
            }).start();
        }
    }
}

class Data4{
    public void func(Integer num){
        synchronized (num){
            System.out.println("start...");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end...");
        }
    }
}

在这里插入图片描述

此时会同步,因为 num = 1 在内存中只有一份,5 个线程争夺同一个资源。

package com.htl.test4;

import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        Data4 data = new Data4();
        for (int i = 0; i < 5; i++) {
            Integer num = Integer.valueOf(i);
            new Thread(()->{
                data.func(num);
            }).start();
        }
    }
}

class Data4{
    public void func(Integer num){
        synchronized (num){
            System.out.println("start...");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end...");
        }
    }
}

在这里插入图片描述

不会同步,因为 5 个线程有 5 个不同的 num,不会产生争夺,而是各用各的。

public class Test {
    public static void main(String[] args) {
        Account account = new Account();
        new Thread(()->{
            account.count();
        },"A").start();
        new Thread(()->{
            account.count();
        },"B").start();
    }
}
class Account{
    private Integer num = 0;
    private Integer id = 0;
    public void count(){
        synchronized (id){
            num++;
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "是第" + num + "位访客");
        }
    }
}

5、ConcurrentModificationException

并发修改异常,集合 List Set Map

package com.htl.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add("a");
                System.out.println(list);
            }).start();
        }
    }
}

当多个线程同时对集合对象进行读写操作的时候,就会抛出 ConcurrentModificationException 异常。

ArrayList 是线程不安全的

在这里插入图片描述

如何解决?使用线程安全的类来完成数据的装载。

1、直接将 ArrayList 替换为 Vector

package com.htl.test;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        List<String> list = new Vector<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add("a");
                System.out.println(list);
            }).start();
        }
    }
}

2、Collections.synchronizedList

package com.htl.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add("a");
                System.out.println(list);
            }).start();
        }
    }
}

3、JUC CopyOnWriteArrayList

package com.htl.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add("a");
                System.out.println(list);
            }).start();
        }
    }
}

CopyOnWrite 写时复制,当我们往一个容器中添加元素的时候,不是直接操作这个容器,而是将原来的容器先复制一份,往复制出来的新容器中添加元素,添加完毕,再将原容器的引用指向新容器,以此来解决并发修改异常,实际上就是实现了读写分离。

在这里插入图片描述

在这里插入图片描述

6、JUC 工具类

CountDownLatch

减法计数器

Java Memory Model Java 内存模型 JMM

工作内存,当一个线程对数据进行操作的时候,不会直接操作主内存,而是会将

主内存中的数据进行复制,复制到工作内存中,线程操作的是工作内存中的数据,操作

完成之后,再将工作内存中的数据同步到主内存中。

package com.htl.test;

import java.util.concurrent.CountDownLatch;

public class Test2 {
    public static void main(String[] args) {

        CountDownLatch countDownLatch = new CountDownLatch(100);

        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println("+++++++++++++++++++++++++Thread");
                countDownLatch.countDown();
            }
        }).start();

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 100; i++) {
            System.out.println("main-------------------------");
        }
    }
}

countDown():计数器减一

await():计数器停止,唤醒其他线程

CyclicBarrier

加法计数器

package com.htl.test;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(30, ()->{
            System.out.println("放行");
        });

        for (int i = 0; i < 90; i++) {
            final int temp = i;
            new Thread(()->{
                //lambda表达式中只能访问final修饰的变量
                System.out.println("-->" + temp);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Semaphore

计数信号量,实现限流操作,限制可以访问某些资源的线程数量。

Semaphore 只有 3 个操作

  • 初始化
  • 获得许可
  • 释放
package com.htl.test;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreTest {
    public static void main(String[] args) {
        //初始化
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 15; i++) {
            new Thread(()->{
                try {
                    //获得许可
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "进店购物");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "出店");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

7、读写锁

ReadWriteLock 接口,实现类 ReentrantReadWriteLock,可以多线程同时读,但是同一时间只能有一个线程进行写入。

读锁是一个共享锁,写锁是一个独占锁。

package com.htl.test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest {
    public static void main(String[] args) {
        Cache2 cache = new Cache2();
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                cache.write(temp,String.valueOf(temp));
            }).start();
        }

        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                cache.read(temp);
            }).start();
        }
    }
}

class Cache2{

    private Map<Integer,String> map = new HashMap<>();
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 写操作
     * @param key
     * @param value
     */
    public void write(Integer key,String value){
        readWriteLock.writeLock().lock();
        System.out.println(key + "开始写入");
        map.put(key, value);
        System.out.println(key + "写入完毕");
        readWriteLock.writeLock().unlock();
    }

    /**
     * 读操作
     * @param key
     */
    public void read(Integer key){
        readWriteLock.readLock().lock();
        System.out.println(key + "开始读取");
        map.get(key);
        System.out.println(key + "读取完毕");
        readWriteLock.readLock().unlock();
    }

}

8、线程池

存放线程对象的缓冲池,为了节约资源。

预先创建一定数量的线程对象,存入缓冲池中,需要用的时候直接从缓冲池中取出,用完不销毁,重新放回到缓冲池中,供下一次请求使用。

优势:

  • 提高线程的利用率
  • 提高响应速度
  • 便于统一管理线程对象
  • 可控制最大并发数

7 大核心参数:

1、corePoolSize 核心池的大小,目前上班的柜台数

2、maximumPoolSize 线程池最大线程数,所有的柜台数(包括上班和不上班的)

3、keepAliveTime:空闲线程的存活时间

4、unit:keepAliveTime 时间单位

5、workQueue:阻塞队列,等候区

6、threadFactory:线程工厂,生成线程对象

7、handler:拒绝任务策略

package com.htl.test;

import java.util.concurrent.*;

public class Test2 {
    public static void main(String[] args) {
        //线程池对象
        ExecutorService executorService = new ThreadPoolExecutor(
                2,
                3,
                1L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        for (int i = 0; i < 6; i++) {
            executorService.execute(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "===>办理业务");
            });
        }

        executorService.shutdown();
    }
}

Executors 提供了三种快速创建线程池的方式

newSingleThreadExecutor():线程池中只有一个线程对象

newFixedThreadPool(5):线程池中有 5 个线程对象

newCachedThreadPool():线程池中线程对象随机分配,由电脑配置决定

package com.htl.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test3 {
    public static void main(String[] args) {
        //单例线程池,只有一个线程对象
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
//        ExecutorService executorService = Executors.newFixedThreadPool(5);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10000; i++) {
            final int temp = i;
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName() + ":" + temp);
            });
        }
        executorService.shutdown();
    }
}

handler 提供了四种拒绝策略

AbortPolicy          直接抛出异常
DiscardPolicy  		 放弃任务,不抛出异常
DiscardOldestPolicy	 尝试与阻塞队列最前面的任务去争夺,不抛出异常
CallerRunsPolicy     谁调用谁处理

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

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

相关文章

【报错解决】vsvars32.bat 不是内部或外部命令,也不是可运行的程序或批处理文件

报错信息&#xff1a; 背景问题&#xff1a;Boost提示 “cl” 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件时&#xff0c;   按照这篇博客的方法【传送】添加了环境变量后&#xff0c;仍然报错&#xff1a; 报错原因&#xff1a; vsvars32.bat 的路径不正…

简单配置,全面保护:HZERO审计服务让安全触手可及

HZERO技术平台&#xff0c;凭借多年企业资源管理实施经验&#xff0c;深入理解企业痛点&#xff0c;为您提供了一套高效易用的审计解决方案。这套方案旨在帮助您轻松应对企业开发中的审计挑战&#xff0c;确保业务流程的合规性和透明度。 接下来&#xff0c;我将为大家详细介绍…

使用nvm对node进行多版本管理

1.nvm下载及安装 下载链接 下载完成后&#xff0c;对文件进行解压安装&#xff0c;按照提示一步步安装&#xff0c;如果电脑上之前有安装过node&#xff0c;需要先卸载&#xff0c;再进行安装。 按照提示完成安装。 2.设置环境变量 可以现在C:\Users\name\AppData\Roamin…

【JavaEE初阶】JavaScript相应的WebAPI

目录 &#x1f332;WebAPI 背景知识 &#x1f6a9;什么是 WebAPI &#x1f6a9;什么是 API &#x1f38d;DOM 基本概念 &#x1f6a9;什么是 DOM &#x1f6a9;DOM 树 &#x1f340;获取元素 &#x1f6a9;querySelector &#x1f6a9;querySelectorAll &#x1f384…

基于MNE的EEGNet 神经网络的脑电信号分类实战(附完整源码)

利用MNE中的EEG数据&#xff0c;进行EEGNet神经网络的脑电信号分类实现&#xff1a; 代码&#xff1a; 代码主要包括一下几个步骤&#xff1a; 1&#xff09;从MNE中加载脑电信号&#xff0c;并进行相应的预处理操作&#xff0c;得到训练集、验证集以及测试集&#xff0c;每个…

SCHEMA find old payroll result

这几天原来HK客户要我帮忙看一个问题&#xff0c;在看HK雇佣条例时&#xff0c;发现又假期是取前12个月的工资&#xff0c;后来查看标准函数发现两个有用的operation&#xff0c;PLOOP与IMPRE 下图是2012年6月工资核算&#xff0c;现在循环着前面4个月。 输入 输出 2012年5月…

【ARM Trace32(劳特巴赫) 使用介绍 1 -- Trace32 debug 工具安装详细步骤】

文章目录 Trace32 工具解压查看安装手册准备安装设置安装目录指定安装目录选择安装类型:选择平台架构开始安装Trace32 应用打开使用界面Trace32 工具解压 使用 7-zip 解压两次: 查看安装手册 安装步骤按照文档中的 1、2、3 进行: 在解压文件中找到安装工具,如下: 准备…

Flux Tools 结构简析

Flux Tools 结构简析 BFL 这次一共发布了 Canny、Depth、Redux、Fill 四个 Tools 模型系列&#xff0c;分别对应我们熟悉的 ControlNets、Image Variation&#xff08;IP Adapter&#xff09;和 Inpainting 三种图片条件控制方法。虽然实现功能是相同的&#xff0c;但是其具体…

【物联网技术与应用】实验3:七彩LED灯闪烁

实验3 七彩LED灯闪烁 【实验介绍】 七彩LED灯上电后&#xff0c;7色动闪光LED模块可自动闪烁内置颜色。它可以用来制作相当吸引人的灯光效果。 【实验组件】 ● Arduino Uno主板* 1 ● USB数据线* 1 ● 7彩LED模块*1 ● 面包板*1 ● 9V方型电池*1 ● 跳线若干 【实验原…

Web 安全必读:跨站脚本攻击 (XSS) 原理与防御指南

文章目录 原理解析&#xff1a;触发方式 文件内容中的xss文件名中的xssHTTP请求中的xss其他 分类&#xff1a; 根据攻击脚本存储的方式根据脚本是否通过服务器处理根据持久性 常见的js触发标签 无过滤情况有过滤情况 xss-labs通关 level1-level10level11-level20 XSS&#x…

Set集合进行!contains判断IDEA提示Unnecessary ‘contains()‘ check

之前写过一个代码&#xff0c;用到了Set集合&#xff0c;判断了如果某个元素不存在就添加到集合中。今天翻看代码又看到了IDEAUnnecessary contains() check爆黄提示。 来一段测试代码&#xff1a; public class SetTest {public static void main(String[] args) {Set<Int…

Mapper代理开发

引入 Mybatis入门方式中&#xff0c;以下代码仍存在硬编码问题 Mapper 代理开发&#xff1a; 目的&#xff1a; 解决原生方式中的硬编码 简化后期执行sql ------下图中&#xff0c;第一段代码是原生硬编码代码块&#xff0c;第二个是引入了Mapper代理开发的代码块。 Mapper代…

abc 384 D(子数组->前缀和) +E(bfs 扩展的时候 按照数值去扩展)

D 做出来的很开心&#xff0c;好像本来就应该做出来>< 思路&#xff1a; 对于连续的子序列&#xff08;也就是 子数组&#xff09; 一般都和 前缀和 后缀和 有关系 区间[l r] 可以用 前缀 S_r -S{l-1} tar来表示。(对于两个元素等于一个数字&#xff0c;就可以枚举一个&…

【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具

工具介绍 toxssin 是一种开源渗透测试工具&#xff0c;可自动执行跨站脚本 (XSS) 漏洞利用过程。它由一个 https 服务器组成&#xff0c;它充当为该工具 (toxin.js) 提供动力的恶意 JavaScript 有效负载生成的流量的解释器。 安装与使用 1、安装需要的依赖库 git clone http…

web网页前后端交互方式

参考该文&#xff0c; 一、前端通过表单<form>向后端发送数据 前端是通过html中的<form>表单&#xff0c;设置method属性定义发送表单数据的方式是get还是post。 如使用get方式&#xff0c;则提交的数据会在url中显示&#xff1b;如使用post方式&#xff0c;提交…

Visual studio的AI插件-通义灵码

通义灵码 TONGYI Lingma 兼容 Visual Studio、Visual Studio Code、JetBrains IDEs 等主流 IDE&#xff1b;支持 Java、Python、Go、C/C、C#、JavaScript、TypeScript、PHP、Ruby、Rust、Scala 等主流编程语言。 安装 打开扩展管理器&#xff0c;搜送“TONGYI Lingma”&…

[C++]类的继承

一、什么是继承 1.定义&#xff1a; 在 C 中&#xff0c;继承是一种机制&#xff0c;允许一个类&#xff08;派生类&#xff09;继承另一个类&#xff08;基类&#xff09;的成员&#xff08;数据和函数&#xff09;。继承使得派生类能够直接访问基类的公有和保护成员&#xf…

【系统】Mac crontab 无法退出编辑模式问题

【系统】Mac crontab 无法退出编辑模式问题 背景一、问题回答1.定位原因&#xff1a;2.确认编辑器类型3.确保编辑器进入正确3.1 确认是否有crontab调度任务3.2 进入编辑器并确保编辑器正常3.3 保存操作 4.确认crontab任务存在 二、后续 背景 之前写过一篇&#xff1a;【系统】…

WPF系列一:窗口设置无边框

WindowStyle 设置&#xff1a;WindowStyle"None"&#xff0c;窗口无法拖拽&#xff0c;但可纵向和横向拉伸 <Window x:Class"WPFDemo.MainWindow.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x&quo…

售电公司办理全指南,开启能源新征程

一、售电公司的重要性 售电公司在能源市场中起着至关重要的作用&#xff0c;成为连接发电企业与终端用户的关键桥梁。随着经济的发展和生活水平的提高&#xff0c;电力需求持续增长&#xff0c;售电公司能够为满足这不断增长的需求提供更多选择。同时&#xff0c;在新电力技术…