当前位置: 首页 > news >正文

Java并发知识点

文章目录

        • 1. start()和run()方法的区别?
        • 2. volatile关键字的作用?
          • 使用volatile能够保证:
          • 防止指令重排
        • 3. sleep方法和wait方法有什么区别?
          • sleep()方法
        • 4. 如何停止一个正在运行的线程?
          • 方法一:
          • 方法二:
        • 5. java如何实现多线程之间的通讯和协作?(如何在两个线程间共享数据?)
          • volatile关键字方式
          • 等待/通知机制
            • 线程的虚假唤醒
          • join方式
          • threadLocal方式
        • 6. 什么是ThreadLocal?
        • 7. Java 中 CountDownLatch 和 CyclicBarrier 有什么不同?
        • 8. 如何避免死锁?
        • 9. Java 中 synchronized 和 ReentrantLock 有什么不同?
        • 10. 有三个线程 T1,T2,T3,怎么确保它们按顺序执行?
          • 方法一:
          • 方法二:


1. start()和run()方法的区别?

JVM执行start()方法,会另起一条线程执行Thread的run()方法,这才起到多线程的效果~
「为什么我们不能直接调用run()方法?」 如果直接调用Thread的run()方法,其方法还是运行在主线程中,没有起到多线程效果。


2. volatile关键字的作用?

volatile,首先想到的是保证内存可见性。客家逆行是对于线程而言的。
在这里插入图片描述
上图是Java内存模型。所有线程的共享变量都放在主内存中,每一个线程都有一个独有的工作内存,每个线程不直接操作在主内存中的变量,而是将主内存上变量的副本放进自己的工作内存中,只操作工作内存中的数据。

当修改完毕后,再把修改后的结果放回到主内存中。每个线程都只操作自己工作内存中的变量,无法直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存来完成。

很明显,在并发环境下一定会发生脏数据问题。

使用volatile能够保证:
  1. 每次读取前必须先从主内存刷新最新的值。
  2. 每次写入后必须立即同步回主内存当中。

也就是说,volatile关键字修饰的变量看到的随时是自己的最新值。

防止指令重排

在基于偏序关系的Happens-Before内存模型中,指令重排技术大大提高了程序执行效率。但是也引入一个新问题:被部分初始化的对象。

例子:

创建一个对象
instance = new Singleton();

它并不是一个原子操作。事实上,它可以”抽象“为下面几条JVM指令:

memory = allocate();    //1:分配对象的内存空间
initInstance(memory);   //2:初始化对象
instance = memory;      //3:设置instance指向刚分配的内存地址

上面操作2依赖于操作1,但是操作3并不依赖于操作2,所以JVM可以以“优化”为目的对它们进行重排序,经过重排序后如下:

memory = allocate();    //1:分配对象的内存空间
instance = memory;      //3:设置instance指向刚分配的内存地址(此时对象还未初始化)
initInstance(memory);   //2:初始化对象

可以看到指令重排之后,操作 3 排在了操作 2 之前,即引用instance指向内存memory时,这段崭新的内存还没有初始化。由于instance已经指向了一块内存空间,从而返回 instance!=null,用户得到了没有完成初始化的“半个”单例。

但是有一点:volatile不保证原子性


3. sleep方法和wait方法有什么区别?

java线程的六种状态:

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 阻塞(BLOCKED):表示线程阻塞于锁。
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
  6. 终止(TERMINATED):表示该线程已经执行完毕。
sleep()方法

该方法会使当前线程进入阻塞状态指定毫秒,当阻塞指定毫秒后,当前线程会重新进入Runnable状态,等待划分时间片。

sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。

wait 方法一般是跟notify方法连用的

多线程之间需要协调工作。如果条件不满足则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制实现依赖于wait/notify或wait/notifyAll。

object.wait()让当前线程进入不可运行状态,如sleep()一样,但不同的是wait方法从一个对象调用,而不是从一个线程调用;我们称这个对象为“锁定对象(lockObj)”。在lockObj.wait()被调用之前,当前线程必须在lockObj上同步(synchronize);然后调用wait()后释放这个锁,并将线程增加到与lockObj相关的“等待名单(wait list)”。然后,另一个在同一个lockObj锁定(synchronize)的方法可以调用lockObj.nofity()。这会唤醒原来等待的线程。基本上,wait() / notify()就像sleep() / interrupt(),只是活动线程不需要直接指向一个睡眠线程,他们只需要共享锁对象(lockObj)。


4. 如何停止一个正在运行的线程?

最直观的一定是 Thread.stop,但是它是不推荐的,并且已经废弃。

方法一:

使用 interrupt 方法中断线程。

interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt方法是在当前线程中打了一个停止标志,并不是真的停止线程。

需要 this.isInterrupted(): 测试线程是否真的已经中断。

方法二:

最好的一种方法,使用标志位停止。

run() 方法中做标识符,保证优雅的停止服务。


5. java如何实现多线程之间的通讯和协作?(如何在两个线程间共享数据?)

使用同步机制让两个线程交替打印1到10的数字,代码如下:

public class DemoThread implements Runnable{private int num = 1;@Overridepublic void run() {while(num<10){synchronized (DemoThread.class){System.out.println(Thread.currentThread().getName() + "-------"+ num++);}}}public static void main(String[] args) {DemoThread thread = new DemoThread();Thread a = new Thread(thread, "线程A");Thread b = new Thread(thread, "线程B");a.start();b.start();}

效果如下:
在这里插入图片描述
结果说明:
因为两个线程的调度完全受CPU时间片的影响,只有当一个线程运行时间结束后,另一个线程才能运行,并不能实现在线程运行的过程中进行切换。

如果我们想让两个线程交替打印数字,那么很显然同步机制是做不到的,这时候就需要两个线程的协作,让两个线程之间进行通信。

volatile关键字方式
  • volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。
等待/通知机制
  • 等待通知机制是基于wait和notify方法来实现的,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被通知或者被唤醒。

也就是通过等待/通知机制让多个线程协作

代码如下:

public class DemoThradNotify implements Runnable{private int num = 1;@Overridepublic void run() {while(true){synchronized(DemoThradNotify.class){DemoThradNotify.class.notify();if (num <= 10){System.out.println(Thread.currentThread().getName() +"------"+ num++);}try{DemoThradNotify.class.wait();} catch(InterruptedException e){e.printStackTrace();}}}}public static void main(String[] args) {DemoThradNotify thread = new DemoThradNotify();Thread a = new Thread(thread, "线程A");Thread b = new Thread(thread, "线程B");a.start();b.start();}
}

效果如下:

在这里插入图片描述

线程的虚假唤醒
public class DemoThradNotifyFake {private static int num = 1;public synchronized void increase(){if (num > 0){try{this.wait();} catch(InterruptedException e){e.printStackTrace();}}num++;System.out.println(Thread.currentThread().getName() +"----" + num);this.notifyAll();}public synchronized void decrease(){if (num == 0){try{this.wait();} catch(InterruptedException e){e.printStackTrace();}}num--;System.out.println(Thread.currentThread().getName()+"----"+num);}public static void main(String[] args) {DemoThradNotifyFake thread = new DemoThradNotifyFake();Thread a = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 4; i++) {thread.increase();}}},"线程A");Thread b = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 4; i++) {thread.decrease();}}},"线程B");Thread c = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 4; i++) {thread.increase();}}},"线程C");Thread d = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 4; i++) {thread.decrease();}}},"线程D");a.start();b.start();c.start();d.start();}
}

图示:
在这里插入图片描述
可以看到即便使用了synchronized关键字,仍然出现了线程安全问题,原因如下:

在某一刻,一个负责增加的线程获得了资源,此时num为1,所以执行 this.wait(); 并等待。

下一刻,另一个负责增加的线程获得了资源,此时num仍然为1,所以再次执行 this.wait(); 并等待。

此后负责减少的线程将num减少到0并唤醒所有等待进程,两个负责增加的线程被唤醒,执行两次增加运算,导致num为2的情况产生。

解决办法就是将 if (num > 0) { 和 if (num == 0) { 中的if换成while。

join方式
  • join其实合理理解成是线程合并,当在一个线程调用另一个线程的join方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行,所以join的好处能够保证线程的执行顺序,但是如果调用线程的join方法其实已经失去了并行的意义,虽然存在多个线程,但是本质上还是串行的,最后join的实现其实是基于等待通知机制的。
threadLocal方式
  • threadLocal方式的线程通信,不像以上三种方式是多个线程之间的通信,它更像是一个线程内部的通信,将当前线程和一个map绑定,在当前线程内可以任意存取数据,减省了方法调用间参数的传递。

6. 什么是ThreadLocal?

定义:线程局部变量是局限于线程内的变量,属于线程自身所有,不在多个线程间共享。java提供 ThreadLocal类 来支持线程局部变量,是一个实现线程安全的方式。

作用:ThreadLocal 是一种以空间换时间的做法,在每一个 Thread 里面维护了一个 ThreadLocal.ThreadLocalMap 把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。

7. Java 中 CountDownLatch 和 CyclicBarrier 有什么不同?

概念:
CountDownLatch 是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。简单来说:CountDownLatch 是一个计数器,可以保证线程之间的顺序执行把线程从并发状态调整为串行状态保证了线程的执行顺序。(只可以使用一次)

CyclicBarrier 是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。典型场景:可以用于多线程计算数据,最后合并计算结果。(可以多次使用)

代码:

package com.javapub.test;import java.util.concurrent.CountDownLatch;/*** @Author: JavaPub* @License: https://github.com/Rodert/* @Contact: https://javapub.blog.csdn.net/* @Date: 2022/1/1 16:50* @Version: 1.0* @Description: countDownLatch 可以保证线程之间的顺序执行把线程从并发状态调整为串行状态保证了线程的执行顺序。* demo效果:当打印完B,再打印C。*/class ThreadA extends Thread {private CountDownLatch down;public ThreadA(CountDownLatch down) {this.down = down;}@Overridepublic void run() {System.out.println("A");try {down.await();//相当于wait(),调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println("C");}
}class ThreadB extends Thread {private CountDownLatch down;public ThreadB(CountDownLatch down) {this.down = down;}@Overridepublic void run() {System.out.println("B");System.out.println(down.getCount());down.countDown();//将count值减1}
}public class Test {public static void main(String[] args) {CountDownLatch down = new CountDownLatch(1);//创建1个计数器new ThreadA(down).start();new ThreadB(down).start();}
}/*输出
A
B
C*/package com.roundyuan.fanggateway.test;import java.util.concurrent.CyclicBarrier;/*** @Author: JavaPub* @License: https://github.com/Rodert/* @Contact: https://javapub.blog.csdn.net/* @Date: 2022/1/2 13:42* @Version: 1.0* @Description: CyclicBarrier*/public class CyclicBarrierDemo {static class TaskThread extends Thread {CyclicBarrier barrier;public TaskThread(CyclicBarrier barrier) {this.barrier = barrier;}@Overridepublic void run() {try {Thread.sleep(1000);System.out.println(getName() + " 到达栅栏 A");barrier.await();System.out.println(getName() + " 冲破栅栏 A");Thread.sleep(2000);System.out.println(getName() + " 到达栅栏 B");barrier.await();System.out.println(getName() + " 冲破栅栏 B");} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {int threadNum = 5;CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " 完成最后任务");}});for (int i = 0; i < threadNum; i++) {new TaskThread(barrier).start();}}
}/*
Thread-3 到达栅栏 A
Thread-1 到达栅栏 A
Thread-4 到达栅栏 A
Thread-2 到达栅栏 A
Thread-0 到达栅栏 A
Thread-2 完成最后任务
Thread-2 冲破栅栏 A
Thread-0 冲破栅栏 A
Thread-4 冲破栅栏 A
Thread-3 冲破栅栏 A
Thread-1 冲破栅栏 A
Thread-4 到达栅栏 B
Thread-0 到达栅栏 B
Thread-2 到达栅栏 B
Thread-1 到达栅栏 B
Thread-3 到达栅栏 B
Thread-3 完成最后任务
Thread-3 冲破栅栏 B
Thread-0 冲破栅栏 B
Thread-4 冲破栅栏 B
Thread-1 冲破栅栏 B
Thread-2 冲破栅栏 B*/

8. 如何避免死锁?

产生死锁就是俩个或多个线程在申请资源时,自己需要的资源别别人持有、并阻塞。所以导致死锁。
解决方法:

  1. 减小锁的范围,尽量保证之锁定自己需要的资源,减小交叉持有资源情况
  2. 但是有些时候不得不持有多个资源,比如银行转账,我们必须同时获得两个账户上的锁,才能进行操作,两个锁的申请必须发生交叉。这时我们也可以打破死锁的那个闭环,在涉及到要同时申请两个锁的方法中,总是以相同的顺序来申请锁,比如总是先申请 id 大的账户上的锁 ,然后再申请 id 小的账户上的锁,这样就无法形成导致死锁的那个闭环。
  3. 我们知道导致死锁有一个因素是阻塞,所以如果我们不使用默认阻塞的锁,也是可以避免死锁的。我们可以使用 ReentrantLock.tryLock() 方法,在一个循环中,如果 tryLock() 返回失败,那么就释放以及获得的锁,并睡眠一小段时间。这样就打破了死锁的闭环。

代码:

public class DeadLock {private static Lock lock1 = new ReentrantLock();private static Lock lock2 = new ReentrantLock();public static void deathLock() {new Thread() {@Overridepublic void run() {while (true) {if (lock1.tryLock()) {try {//如果获取成功则执行业务逻辑,如果获取失败,则释放lock1的锁,自旋重新尝试获得锁if (lock2.tryLock()) {try {System.out.println("Thread1:已成功获取 lock1 and lock2 ...");break;} finally {lock2.unlock();}}} finally {lock1.unlock();}}System.out.println("Thread1:获取锁失败,重新获取---");try {//防止发生活锁TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {@Overridepublic void run() {while (true) {if (lock2.tryLock()) {try {//如果获取成功则执行业务逻辑,如果获取失败,则释放lock2的锁,自旋重新尝试获得锁if (lock1.tryLock()) {try {System.out.println("Thread2:已成功获取 lock2 and lock1 ...");break;} finally {lock1.unlock();}}} finally {lock2.unlock();}}System.out.println("Thread2:获取锁失败,重新获取---");try {//防止发生活锁TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 5; i++) {deathLock();}}
}

9. Java 中 synchronized 和 ReentrantLock 有什么不同?

等待可中断:

使用synchronized,不能被中断。synchronized 也可以说是Java提供的原子性内置锁机制。内部锁扮演了互斥锁(mutual exclusion lock ,mutex)的角色,一个线程引用锁的时候,别的线程阻塞等待。

使用ReentrantLock。等待了很长时间以后,可以中断等待,转而去做别的事情。

公平锁:

公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次获得锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会获得锁。 synchronized的锁是非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过带布尔值的构造函数要求使用公平锁。

还有大家已知的俩点:

  • synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
  • synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

10. 有三个线程 T1,T2,T3,怎么确保它们按顺序执行?

方法一:

线程内部顺序调用,T1、T2、T3。这个可能不是要考察的点,但也是一个方案。

方法二:

join()方法用于将线程由 ”并行“变成”串行“,它用于等待其他线程的终止,在当前线程掉用了join()方法,那么当前线程将进入阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转变成就绪状态,等待CPU的使用权。

public class Test {public static void main(String[] args) {ThreadA threadA = new ThreadA();ThreadB threadB = new ThreadB(threadA);ThreadC threadC = new ThreadC(threadB);threadA.start();threadB.start();threadC.start();}}class ThreadA extends Thread {@Overridepublic void run() {System.out.println("线程A");}
}class ThreadB extends Thread {Thread threadA;public ThreadB() {// TODO Auto-generated constructor stub}public ThreadB(Thread threadA) {this.threadA = threadA;}@Overridepublic void run() {try {threadA.join();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("线程B");}
}class ThreadC extends Thread {Thread threadB;public ThreadC(Thread threadB) {this.threadB = threadB;}@Overridepublic void run() {try {threadB.join();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("线程C");}
}线程A
线程B
线程C

相关文章:

Java并发知识点

文章目录1. start()和run()方法的区别&#xff1f;2. volatile关键字的作用&#xff1f;使用volatile能够保证&#xff1a;防止指令重排3. sleep方法和wait方法有什么区别&#xff1f;sleep()方法4. 如何停止一个正在运行的线程&#xff1f;方法一&#xff1a;方法二&#xff1…...

前端 ES6 环境下 require 动态引入图片以及问题

前端 ES6 环境下 require 动态引入图片以及问题require 引入图片方式打包体积对比总结ES6 环境中&#xff0c;通过 require 的方式引入图片很方便&#xff0c;一直以来也没有出过什么问题&#xff0c;后来项目中&#xff0c;需要动态引入图片。 require 动态引入也容易实现&am…...

PCL 欧氏聚类分割

文章目录 一、应用背景1、点云分割算法的属性2、点云分割的挑战二、实现过程三、主要函数及代码实现1、主要函数2、核心代码3、效果实现四、参考文献一、应用背景 1、点云分割算法的属性 1)鲁棒性,比如树木是具有与汽车相区别的特征的,当点云数据的特征数量增加时,分割算…...

一台服务器最大能支持多少条TCP连接

一、一台服务器最大能打开的文件数 1、限制参数 我们知道在Linux中一切皆文件&#xff0c;那么一台服务器最大能打开多少个文件呢&#xff1f;Linux上能打开的最大文件数量受三个参数影响&#xff0c;分别是&#xff1a; fs.file-max &#xff08;系统级别参数&#xff09;&am…...

Teradata退出中国,您可以相信中国数据库!

继Adobe、Tableau、Salesforce之后&#xff0c;2023年2月15日&#xff0c;数仓软件巨头Teradata宣布将逐步结束在中国的直接运营。数仓界的“黄埔军校”仓皇撤出中国市场给出的理由非常含蓄&#xff1a;Teradata对中国当前和未来商业环境的慎重评估&#xff0c;我们做了一个艰难…...

markdown组合数学公式

markdown组合数学公式 $C_n^m$CnmC_n^mCnm​ $A_n^m$AnmA_n^mAnm​ $$\binom{m}{nm1}$$(mnm1)\binom{m}{nm1}(nm1m​) $${m\choose nm1}$$(mnm1){m\choose nm1}(nm1m​)...

Golang实践录:一个字符串比较示例

本文介绍两个含中文的字符串且针对相同位置字符的比较&#xff0c;给出实现代码。 起因 某工程需将接收的字符串和数据库里的指定字段值对比&#xff0c;该字符串含中文&#xff0c;两者允许个别字符有差异&#xff0c;差异数量3及以下的&#xff0c;认为相同。 字符串默认用…...

Linux后台开发工具箱-葵花宝典

目录目录 11. 前言 52. 脚本类工具 52.1. 双引号和单引号 52.2. 环境变量和变量 52.3. sed命令-字符串文本操作 62.4. sed和awk使用外部变量 62.5. awk 应用 62.5.1. awk给外部变量赋值 62.5.2. awk 多字符串分割 72.6. 日期操作 72.7. 设置shell模式 82.8. 设置shell提示 82.9…...

http的请求上下文

1.引入&#xff1a; 上下文是指HTTP框架为每个HTTP请求所准备的结构体。 HTTP框架定义的这个上下文是针对于HTTP请求的&#xff0c; 而且一个HTTP请求对应于每一个HTTP模块都可以有一个独立的上下文结构体&#xff08;并不是一个请求的上下文由所有HTTP模块共用&#xff09; 。…...

【MySQL】MySQL表的增删改查(进阶)

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;MySQL数据库&#x1f447; ✨算法专栏&#xff1a;算法基础&#x1f447; ✨每日一语&#xff1a;悟已往之不谏&#xff0c;知来者之可追。实迷途其未远&#xff0c;觉今是而昨非。 目 录&#x1f384;一. 数…...

C++ Primer Plus习题及答案-第十八章

习题选自&#xff1a;C Primer Plus(第六版) 内容仅供参考&#xff0c;如有错误&#xff0c;欢迎指正 ! C decltype和返回类型后置 左右值引用和移动语义 C11 新的类功能 C11 Lambda表达式 C11 包装器function 复习题 1. 使用用大括号括起的初始化列表语法重写下述代码。重写后…...

Redis事务控制

1.Redis事务控制的相关命令 命令名作用MULTI表示开始收集命令&#xff0c;后面所有命令都不是马上执行&#xff0c;而是加入到一个队列中。EXEC执行MULTI后面命令队列中的所有命令。DISCARD放弃执行队列中的命令。WATCH“观察“、”监控“一个KEY&#xff0c;在当前队列外的其…...

Springcloud OpenFeign 详解

一、概述OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解&#xff0c;整合了hystrix&#xff0c;同时&#xff0c;可以和Eureka和ribbon配合使用&#xff0c;如RequestMapping等等。OpenFeign的FeignClient可以解析SpringMVC的RequestMapping注解下的接口&#xf…...

软件测试期末

考原题就是爽 软件测试技术 知识点整理 https://wenku.baidu.com/view/524c900f4b2fb4daa58da0116c175f0e7cd11913.html 关键知识点 https://www.cnblogs.com/whylaughing/category/813559.html?page1 边界值法不选择无效数据 边界值分析法的基本思想 选取正好等于&am…...

关于Java的深拷贝和浅拷贝

文章目录1.拷贝的引入1.1引用拷贝1.2对象拷贝2.深拷贝与浅拷贝2.1浅拷贝2.2深拷贝1.拷贝的引入 1.1引用拷贝 创建一个指向对象的引用变量的拷贝 Teacher teacher new Teacher("Taylor",26); Teacher otherteacher teacher; System.out.println(teacher); System…...

固定值电阻的检测方法总结

🏡《总目录》 目录 1,概述2,测量方法3,检测方法3.1,读值3.2,测量3.3,排故4,总结1,概述 本文简单总结固定值电阻的测量与检查方法要点和注意事项。 2,测量方法 对于固定值电阻的测量来讲,直接将万用表红黑表笔分别插入到如下图所示的红色和黑色接线端。然后将万用表…...

打印机相关

打印机相关 打印机协议 ipp,printer-job-language,lpd协议。他们的默认端口分别是631,9100和515. printer-job-language(RAW协议) 9100端口的printer-job-language,又称为RAW协议。目前遇到的问题是,此端口发送数据,打印机直接打印,除非发送正确的printer-job-lan…...

入门力扣自学笔记235 C++ (题目编号:2347)

2347. 最好的扑克手牌 题目&#xff1a; 给你一个整数数组 ranks 和一个字符数组 suit 。你有 5 张扑克牌&#xff0c;第 i 张牌大小为 ranks[i] &#xff0c;花色为 suits[i] 。 下述是从好到坏你可能持有的 手牌类型 &#xff1a; "Flush"&#xff1a;同花&…...

k8s-二进制部署

文章目录一、环境二、步骤1、安装cfssl工具2、部署etcd集群3、在node节点安装docker组件4、安装flannel组件部署master节点组件部署node节点部署kube-proxy组件三、测试一、环境 角色服务器地址组件master192.168.174.140kube-apiserver&#xff0c;kube-controller-manager&a…...

前缀和差分(C/C++)

目录 1. 前缀和的定义 2. 一维前缀和 2.1 计算公式 2.2 用途 2.3 小试牛刀 3. 二维前缀和 3.1 用途 1. 前缀和的定义 对于一个给定的数列A&#xff0c;他的前缀和数中 S 中 S[ i ] 表示从第一个元素到第 i 个元素的总和。 如下图&#xff1a;绿色区域的和就是前缀和数组…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

C# WPF 左右布局实现学习笔记(1)

开发流程视频&#xff1a; https://www.youtube.com/watch?vCkHyDYeImjY&ab_channelC%23DesignPro Git源码&#xff1a; GitHub - CSharpDesignPro/Page-Navigation-using-MVVM: WPF - Page Navigation using MVVM 1. 新建工程 新建WPF应用&#xff08;.NET Framework) 2.…...

React、Git、计网、发展趋势等内容——前端面试宝典(字节、小红书和美团)

React React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么&#xff0c;Fiber架构&#xff0c;面试向面试官介绍&#xff0c;详细解释 用户: React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么&#xff0c;Fiber架构&#xff0c;面试向面试官介绍&#x…...