Java EE--多线程(一)

news2024/11/23 20:14:50

目录

一、认识多线程

1.1 概念

(1) 线程是什么

 (2)为啥要有线程?

 (3) 进程和线程的区别

(4)Java 的线程和操作系统线程的关系

 

1.2 创建线程

方法1 继承 Thread 类

方法2 实现Runnable接口

使用Runnable定义任务有啥好处?

其他变形:

1.3 Thread 类及常见方法

(1)Thread 的常见构造方法

 ​编辑

(2) Thread 的几个常见属性

 

(3)启动一个线程-start()

(4)中断一个线程

 (5) 线程等待

 (6) 获取当前线程

(7)休眠当前线程

1.4 线程的状态

二、多线程带来的风险--线程安全

2.1 什么是线程安全?

2.2 线程不安全的原因

(1) 修改共享数据

(2)线程是抢占是执行的

(3)原子性

(4)内存可见性

 (5)有序性

三、Synchronized&volatile&wait¬ify的使用

3.1 Sychronized锁

Sychronized的使用

 Sychronized的特性

 

3.2  volatile 关键字

3.3 wait 和 notify

wait()方法

notify()方法

notifyAll()方法


一、认识多线程

1.1 概念

(1) 线程是什么

一个线程就是一个 " 执行流 ". 每个线程之间都可以按照顺讯执行自己的代码 . 多个线程之间 " 同时 " 执行着多份代码.

 举个栗子:

我们设想如下场景:
一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。
如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。
此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别排队执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread )。

 (2)为啥要有线程?

首先 , " 并发编程 " 成为 " 刚需 ".
单核 CPU 的发展遇到了瓶颈 . 要想提高算力 , 就需要多核 CPU. 而并发编程能更充分利用多核 CPU
资源 .
有些任务场景需要 " 等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作 , 也需要用到并发编
.
其次 , 虽然多进程也能实现 并发编程 , 但是线程比进程更轻量 .
创建线程比创建进程更快 .
销毁线程比销毁进程更快 .
调度线程比调度进程更快 .
最后 , 线程虽然比进程轻量 , 但是人们还不满足 , 于是又有了 " 线程池 "(ThreadPool) " 协程 "
(Coroutine)
面试题:什么是并发?什么是并行?
并发:一会干这件事,一会干那件事(同时只能干一件事)
并行:一边干这件事,一边干那件事(真正意义上的同时进行)
在编程方面,我们并不区分是并行还是并发,统一称为并发编程。

 举个栗子:

 (3) 进程和线程的区别

  • 进程是包含线程的. 每个进程至少有一个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.
  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
比如多进程例子中,每个客户来银行办理各自的业务,但他们之间的票据肯定是不想让别 人知道的,否则钱不就被其他人取走了么。而上面我们的公司业务中,张三、李四、王五虽然是不同的执行流,但因为办理的都是一家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最大区别。

 

(4)Java 的线程和操作系统线程的关系

线程是操作系统中的概念 . 操作系统内核实现了线程这样的机制 , 并且对用户层提供了一些 API 供用户使用( 例如 Linux pthread ).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装 .


1.2 创建线程

方法1 继承 Thread

(1)  继承 Thread 来创建一个线程类 .
public class Demo01_Thread {
    public static void main(String[] args) {
        // 创建自己定义的线程对象
        MyThread thread = new MyThread();
        // 执行这个线程 start方法是启动线程,并通知操作系统加入CPU调度
        thread.start();

    }
}

// 继承Thread类并实现run方法
class MyThread extends Thread {
    // run方法中的代码,就表示线程要执行的任务
    @Override
    public void run() {
        System.out.println("hello thread...");
    }
}
调用start()方法之后,JVM会调用系统API并在系统中生成一个PCB来执行run()方法中的代码

方法2 实现Runnable接口

(1)实现Runnable接口并重写run()
 

public class Demo03_Runnable {
    public static void main(String[] args) {
        // 创建Runnable对象
        MyRunnable runnable = new MyRunnable();
        // 创建线程
        Thread thread = new Thread(runnable);
        // 启动线程,参与CPU调度
        thread.start();
    }

}
// 实现Runnable接口
class MyRunnable implements Runnable {

    // 表示的是线程要执行的任务
    @Override
    public void run() {
        while (true) {
            System.out.println("生产皮包,金币+1...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

使用Runnable定义任务有啥好处?

  • 解耦:把定义线程,与定义任务分开(把不同的功能都给分开,如果要修改或查找相应的功能的时候可以直接在指定的位置查找)
  • 把创建线程,与定义任务分开,以便修改代码时,可以统一修改

其他变形:

  • 匿名内部类创建 Thread 子类对象
    // 使用匿名类创建 Thread 子类对象
    Thread t1=new Thread() {
        @Override
        public void run() {
            System.out.println("使用匿名类创建 Thread 子类对象");    
       }
    };
    • 匿名内部类创建 Runnable 子类对象
    // 使用匿名类创建 Runnable 子类对象
    Thread t2=new Thread(newRunnable() {
        @Override
        public void run() {
            System.out.println("使用匿名类创建 Runnable 子类对象");    
       }
    });
    • lambda 表达式创建 Runnable 子类对象
    Thread t3=new Thread(() ->System.out.println("使用匿名类创建 Thread 子类对象"));
    Thread t4=new Thread(() -> {
        System.out.println("使用匿名类创建 Thread 子类对象");
    });

    1.3 Thread 类及常见方法

    Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
    用我们上面的例子来看,每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

(1)Thread 的常见构造方法

 

 Thread t1=new Thread();
Thread t2=new Thread(newMyRunnable());
Thread t3=new Thread("这是我的名字");
Thread t4=new Thread(newMyRunnable(), "这是我的名字");

(2) Thread 的几个常见属性

public class Demo10_Properties {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "大圣");
        // 启动线程
        thread.start();

        // 打印Thread对象中的属性
        System.out.println("线程Id = " + thread.getId());
        System.out.println("线程名 = " + thread.getName());
        System.out.println("线程状态 = " + thread.getState());
        System.out.println("线程优先级 = " + thread.getPriority());
        System.out.println("线程是否后台 = " + thread.isDaemon());
        System.out.println("线程是否存活 = " + thread.isAlive());
        System.out.println("线程是否中断 = " + thread.isInterrupted());
    }
}

(3)启动一个线程-start()

之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。

  • 覆写 run 方法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是把李四、王五叫过来了
  • 而调用 start() 方法,就是喊一声:行动起来!“,线程才真正独立去执行了。

调用 start 方法 , 才真的在操作系统的底层创建出一个线程 .

(4)中断一个线程

 举个栗子
李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。
目前常见的有以下两种方式
1. 通过是否中断的标志位
public class Demo13_Interrupted01 {
    // 定义一个中断标识
    private static boolean isQuit = false;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!isQuit) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程任务结束");
        });
        // 启动线程
        thread.start();
        // 主线程休眠3秒,模拟子线程正在处理任务
        Thread.sleep(3000);
        // 设置中断标志位为true
        isQuit = true;
        // 让子线程先结束
        Thread.sleep(1000);
        System.out.println("主线程结束");

    }
}
2. 调用Thread类提供的 interrupted() 方法
public class Demo14_Interrupted02 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            // 注意判断条件
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 异常处理来中断线程
                    // 方式一:啥也不做
                    // 方式二:处理具体的逻辑
                    // 方式三:真正的中断
                    break;
                }
            }
            System.out.println("是否中断:" + Thread.currentThread().isInterrupted());
            System.out.println("线程任务结束");
        });
        // 启动线程
        thread.start();
        // 主线程休眠3秒,模拟子线程正在处理任务
        Thread.sleep(3000);
        // 中断线程,修改Thread中的中断标志
        thread.interrupt();
        // 让子线程先结束
        Thread.sleep(1000);
        System.out.println("主线程结束");
    }
}

 (5) 线程等待

举个栗子:
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnabletarget= () -> {
            for (inti=0; i<10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() 
                                       +": 我还在工作!");
                    Thread.sleep(1000);
                } catch (InterruptedExceptione) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() +": 我结束了!");        
};
  Threadthread1=newThread(target, "李四");
        Threadthread2=newThread(target, "王五");
        System.out.println("先让李四开始工作");
        thread1.start();
        thread1.join();
        System.out.println("李四工作结束了,让王五开始工作");        
        thread2.start();
        thread2.join();
        System.out.println("王五工作结束了");
    }
}

 (6) 获取当前线程

   返回当前线程的引用

public class ThreadDemo {
    public static void main(String[] args) {
        Threadthread=Thread.currentThread();       
       System.out.println(thread.getName());
    }
}

(7)休眠当前线程

也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。
public class ThreadDemo {
    public static void main(String[] args) throwsInterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3*1000);
        System.out.println(System.currentTimeMillis());
    }
}

1.4 线程的状态

 

 


二、多线程带来的风险--线程安全

2.1 什么是线程安全?

想给出一个线程安全的确切定义是复杂的,但我们可以这样认为:
如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线 程安全的。

2.2 线程不安全的原因

(1) 修改共享数据

多个线程修改了同一个共享变量

多个线程修改不同的变量,不会出现线程安全问题。多个线程读取同一个变量,也不会出现线程安全的问题,单线程环境下也不会出现线程安全问题。

(2)线程是抢占是执行的

多个线程在CPU上调度是随机的,顺序是不可预知的

(3)原子性

什么是原子性?

我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。

那我们应该如何解决这个问题呢?是不是只要给房间加一把锁, A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。
有时也把这个现象叫做同步互斥,表示操作是互相排斥的。

要么全都执行,要么全都不执行

(4)内存可见性

多线程环境下,某一线程修改的共享变量的值,另一个线程没有感受到最新的值

引入JAVA内存模型的概念JMM

1、主内存指的是硬件的内存条进程在启动的时候会申请一些资源,包括内存资源用来保存所有的变量,

2、工作内存指的是线程独有的内存空间,他们之间不能够相互访问,起到了线程之间内存隔离的作用,

3、JMM规定一个线程在修改某个变量的值时,必须把这个变量从主内存中加载到自己的工作内存,修改完成后再修,再刷新回主内存

4、每个工作内存之间是相互隔离的。

为什么要用JMM?

因为JAVA是一个跨平台的语言,把不同的计算设备和操作系统对内存的管理做了一个统一的封装。

 (5)有序性

编译过程,JVM调用本地接口CPU执行指令过程中指令的有序性。指令在特殊情况下会打乱顺序,并不是按程序员的预期去执行的。

 

 

三、Synchronized&volatile&wait&notify的使用

3.1 Sychronized锁

  •  加锁之后可以把多线程并发执行并成单线程串行执行
  • 由于是单线程执行的,所以第二个线程读到的值一定是第一个线程修改过的值,从而实现的内存可见
  • Sychronized可以解决原子性内存可见性,但是不能解决有序性问题

Sychronized的使用

synchronized 本质上要修改指定对象的 " 对象头 ". 从使用角度来看 , synchronized 也势必要搭配一个具体的对象来使用.
1) 直接修饰普通方法 : 锁的 SynchronizedDemo 对象
public class SynchronizedDemo {
    public synchronized void methond() {
    }
}
2) 修饰静态方法 : 锁的 SynchronizedDemo 类的对象
public class SynchronizedDemo {
    public synchronized static void method() {
    }
}
3) 修饰代码块 : 明确指定锁哪个对象 .
   锁当前对象
public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            
        }
    }
}
锁类对象
public class SynchronizedDemo {
    public void method() {
        synchronized (SynchronizedDemo.class) {
       }    
   }
}

 Sychronized的特性

1) 互斥
synchronized 会起到互斥效果 , 某个线程执行到某个对象的 synchronized 中时 , 其他线程如果也执行到同一个对象 synchronized 就会 阻塞等待 .
2) 刷新内存
synchronized 的工作过程 :
1. 获得互斥锁
2. 从主内存拷贝变量的最新副本到工作的内存
3. 执行代码
4. 将更改后的共享变量的值刷新到主内存
5. 释放互斥锁
3) 可重入

3.2  volatile 关键字

volatile 能保证内存可见性 有序性 volatile synchronized 有着本质的区别 . synchronized 能够保证原子性 , volatile 不保证原子性

代码在写入 volatile 修饰的变量的时候,
  • 改变线程工作内存中volatile变量副本的值
  • 将改变后的副本的值从工作内存刷新到主内存
代码在读取 volatile 修饰的变量的时候 ,
  •  从主内存中读取volatile变量的最新值到线程的工作内存中
  • 从工作内存中读取volatile变量的副本

保证内存可见性

创建两个线程 t1 t2
t1 中包含一个循环 , 这个循环以 flag == 0 为循环条件 .
t2 中从键盘读入一个整数 , 并把这个整数赋值给 flag.
预期当用户输入非 0 的值的时候 , t1 线程结束 .
static class Counter {
    public int flag=0;
}
public static void main(String[] args) {
    Counter counter=new Counter();
    Thread t1=new Thread(() -> {
        while (counter.flag==0) {
            // do nothing
        }
        System.out.println("循环结束!");
    });
    Thread t2=new Thread(() -> {
        Scanner scanner=new Scanner(System.in);        
       System.out.println("输入一个整数:");
        counter.flag=scanner.nextInt();
    });
    t1.start();    
    t2.start();}

不保证原子性

increase 方法去掉 synchronized
count 加上 volatile 关键字 .
static class Counter {
    volatile public int count=0;
void increase() {
        count++;
    }
}
public static void main(String[] args) throwsInterruptedException {
    final Counter counter=new Counter();
  Thread t1=new Thread(() -> {
        for (int i=0; i<50000; i++) {
            counter.increase();
        }
    });
    Thread t2=new Thread(() -> {

        for (int i=0; i<50000; i++) {
            counter.increase();
        }
    });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(counter.count);}
//此时可以看到, 最终 count 的值仍然无法保证是 100000.  

3.3 wait notify

由于线程之间是抢占式执行的 , 因此线程之间执行的先后顺序难以预知 .
但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序 .
举个栗子:
球场上的每个运动员都是独立的 " 执行流 " , 可以认为是一个 " 线程 ".
而完成一个具体的进攻得分动作 , 则需要多个运动员相互配合 , 按照一定的顺序执行一定的动作 , 线程1 " 传球 " , 线程 2 才能 " 扣篮 ".

 完成这个协调工作, 主要涉及到三个方法

wait() / wait(long timeout): 让当前线程进入等待状态.

notify() / notifyAll(): 唤醒在当前对象上等待的线程.
注意 : wait, notify, notifyAll 都是 Object 类的方法 .

wait()方法

wait 做的事情:

使当前执行代码的线程进行等待. (把线程放到等待队列中)

释放当前的锁满足一定条件时被唤醒, 重新尝试获取这个锁 .
wait 要搭配 synchronized 来使用 . 脱离 synchronized 使用 wait 会直接抛出异常
wait 结束等待的条件 :
  • 其他线程调用该对象的 notify 方法.
  • wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
public static void main(String[] args) throwsInterruptedException {
    Object object=new Object();
    synchronized (object) {
        System.out.println("等待中");
        object.wait();
        System.out.println("等待结束");
    }
}

notify()方法

notify 方法是唤醒等待的线程.
  • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
  • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到")
  • notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行
        完,也就是退出同步代码块之后才会释放对象锁。\
wait()和notify()搭配使用
wait()是让线程进入休眠阶段,notify()去唤醒等待的线程 与锁强相关

 sleep(long)让线程进入休眠状态 在指定的时间内不会被调度到CPU上执行

 

 

 

notifyAll()方法

notify方法只是唤醒某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程.

 

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

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

相关文章

数据结构学习记录——哈夫曼树(什么是哈夫曼树、哈夫曼树的定义、哈夫曼树的构造、哈夫曼树的特点、哈夫曼编码)

目录 什么是哈夫曼树 哈夫曼树的定义 哈夫曼树的构造 图解操作 代码实现 代码解析 哈夫曼树的特点 哈夫曼编码 不等长编码 二叉树用于编码 哈夫曼编码实例 什么是哈夫曼树 我们先举个例子&#xff1a; 要将百分制的考试成绩转化成五分制的成绩 if(score < …

ASEMI代理ADV7391BCPZ原装ADI车规级ADV7391BCPZ

编辑&#xff1a;ll ASEMI代理ADV7391BCPZ原装ADI车规级ADV7391BCPZ 型号&#xff1a;ADV7391BCPZ 品牌&#xff1a;ADI /亚德诺 封装&#xff1a;LFCSP-32 批号&#xff1a;2023 安装类型&#xff1a;表面贴装型 引脚数量&#xff1a;32 工作温度:-40C~85C 类型&…

ChatGPT实现markdown 格式与 emoji 表情

markdown 格式与 emoji 表情 书写文章时&#xff0c;巧妙的使用一些小图标&#xff0c;可以给文章增加不少的灵动感&#xff0c;读者也会感觉更加轻松。恰当的图标也能增进读者对内容的理解。ChatGPT 目前不能直接联网&#xff0c;但可以使用 emoji 表情文字来达到类似的效果。…

笔记本电脑开机黑屏没反应怎么办?

笔记本电脑开机黑屏没反应怎么办&#xff1f;有用户电脑开机之后&#xff0c;桌面会变成黑屏显示。而且是常常都会出现这样的问题&#xff0c;非常影响自己的电脑使用体验。那么遇到这个问题要怎么去进行问题的解决呢&#xff1f;来看看以下的解决方法吧。 准备工作&#xff1a…

超级详细的mysql数据库安装指南

MySql数据库 如果你的电脑是mac那么你看这位大佬的分享。 如果你的电脑是windows&#xff0c;参考下面的安装步骤。 一、下载mysql数据库&#xff1f; 进入MySQL官方网站&#xff08;MySQL Community Downloads&#xff09;&#xff0c;按下图顺序点击 1、进入下载页面 2、…

Dom树,什么是dom树?

相信很多初学前端的小伙伴&#xff0c;学了html, css, js之后&#xff0c;会遇到 一个名词 DOM树。 首先说一下DOM是什么&#xff1f; DOM 是 Document Object Model&#xff08;文档对象模型&#xff09;的缩写。 举个例子 我们日常生活中&#xff0c;经常会遇到一些写文档…

Spring Cloud第二季--消息驱动Spring Cloud Stream

文章目录 什么是Spring Cloud StreamStream 原理 牛刀小试消息重复消费问题 什么是Spring Cloud Stream Spring Cloud Stream is a framework for building highly scalable event-driven microservices connected with shared messaging systems. The framework provides a fl…

linux系统函数的运用

函数 函数详解函数的作用函数的定义函数的返回值函数的作用范围函数传参函数递归函数库 函数详解 函数的作用 在编写shell脚本的时候&#xff0c;经常会发现在多个地方使用了同一段代码&#xff0c;如果只是一小段代码&#xff0c;一般也无关紧要&#xff0c;但是要在脚本中多…

如何禁止电脑运行游戏?

在休息的时候&#xff0c;很多人都喜欢使用电脑玩游戏来消磨时间&#xff0c;但是对于未成年人来说&#xff0c;很容易沉迷游戏&#xff0c;从而影响正常的学业和成长。那么如何才能禁止电脑运行游戏呢&#xff1f;下面我们就来了解一下。 除了将电脑游戏卸载之外&#xff0c;还…

markdown甘特图语法介绍

1. 介绍 甘特图&#xff08;Gantt chart&#xff09;又称为横道图、条状图(Bar chart)。其通过条状图来显示项目、进度和其他时间相关的系统进展的内在关系随着时间进展的情况。 2. 语法 1. 代码通用语法 在 markdown 的 代码模块 中编写甘特图&#xff0c;类型是mermaid&a…

华硕ROG|玩家国度魔霸新锐2023 Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原

华硕ROG|玩家国度魔霸新锐2023 Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原 文件地址&#xff1a;https://pan.baidu.com/s/1snKOsH3OMl3GZLqeAf-GLA?pwd8888 华硕工厂恢复系统 &#xff0c;安装结束后带隐藏分区以及机器所有驱动软件 需准备一个16G左右…

第三十二章 React路由组件的简单使用

1、NavLink的使用 一个特殊版本的 Link&#xff0c;当它与当前 URL 匹配时&#xff0c;为其渲染元素添加样式属性 <NavLink className"list-group-item" to"/home">Home</NavLink> <NavLink className"list-group-item" to&quo…

Python 密码破解指南:0~4

协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【OpenDocCN 饱和式翻译计划】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 收割 SB 的人会被 SB 们封神&#xff0c;试图唤醒 SB 的人是 SB 眼中的 SB。——SB 第三定律 零、简…

stream笔记

1、 创建流stream 1.1、 Stream 的操作三个步骤 1.2、 stream中间操作 1.2.1 、 limit、skip、distinct 1.2.2、 map and flatMap 1.2.3、 sort 自然排序和定制排序 1.3、 add and andAll difference: 1.4、 终止操作 1.4.1、 allmatch、anyMatch、noneMatch、max、min…

JavaScript通过js的方式来判断一个数奇偶性的代码

以下为通过js的方式来判断一个数奇偶性的程序代码和运行截图 目录 前言 一、通过js的方式来判断一个数奇偶性&#xff08;html部分&#xff09; 1.1 运行流程及思想 1.2 代码段 二、通过js的方式来判断一个数奇偶性&#xff08;js部分&#xff09; 2.1 运行流程及思想 2…

美国新的 AI 研究基金将专注于 6 个领域

美国白宫周四宣布&#xff0c;在 1.4 亿美元的联邦资金支持下&#xff0c;拜登政府将开设七个新的人工智能实验室。 国家科学基金会将在其他政府机构的支持下掌舵运营。这些研究所将专注于六个研究课题&#xff1a; 1. 值得信赖的人工智能&#xff0c;隶属于马里兰大学领导的…

JustFE团队前端代码规范,降本增效,敏感肌可用

背景 &#x1f30f; 近年来&#xff0c;接手各个前端的代码&#xff0c;看着前人屎山&#xff0c;深恶痛绝 为了避免自己或者团队&#xff0c;继续添粪&#xff0c;因此经验总结一番~ 规范化优点&#xff1a; 容易理解&#xff0c;维护性强容易编写&#xff0c;扩展性强精准定…

优化营商环境:打造政策精准服务平台,提高惠政策落实落地时效性

近年来&#xff0c;各级政府部门及产业园区不断加强对于惠企政策的宣传和落实&#xff0c;努力打造优质的营商环境&#xff0c;加大助企纾困力度&#xff0c;以推动经济高质量发展。为了更好地实现这一目标&#xff0c;搭建惠企政策精准服务平台成为了一个非常重要的举措。 搭建…

Apache Zeppelin系列教程第四篇——Interpreter原理分析

Interpreter 其实就是整个项目的核心&#xff0c;代码运行都是在里面进行执行的&#xff0c;首先来看下Interpreter的抽象类 以jdbc-Interpreter为例&#xff0c;可以参考下测试代码(这个代码里面可以直接测试jdbc的sql执行过程和数据返回过程) 参数和介绍可以参考官方文档&am…

CH9329双头线使用说明

1.介绍说明 CH9329双头线是集成了CH9329CH340芯片的成品线&#xff0c;主要作用是使用主控电脑发送串口指令数据来控制被控电脑的键盘以及鼠标的功能。主控电脑和被控电脑可以是同一台电脑&#xff0c;就是能够自己控制自己。支持自定义修改USB的硬件信息&#xff0c;如PID、V…