【Java集合篇】ConcurrentHashMap是如何保证fail- safe的

在这里插入图片描述

ConcurrentHashMap是如何保证fail-safe的

  • ✅典型解析
  • ✅拓展知识仓
    • ✅分段锁
    • ☑️分段锁适用于什么情况
    • 🟡分段锁的锁争用情况,是否会带来一定的性能影响
    • ✔️分段锁的优缺点
    • 🟢 还有哪些其他的线程安全哈希表实现
    • 🟠Hashtable和 Collections区别
    • 🟡分段锁和锁之间的区别是什么
    • 🟢分段锁比锁更加安全吗
    • 🟢弱一致性保障


✅典型解析


在JDK 1.8 中,ConcurrentHashMap 作为一个并发容器,他是解决了 fail-fast 的问题的,也就是说他是一个 fail-safe 的容器。通过以下两种机制来实现 fail-safe 特性:


首先,在 ConcurrentHashMap 中,遍历操作返回的是弱一致性选代器,这种跌代器的特点是,可以获取到在选代器创建前被添加到 ConcurrentHashMap 中的元素,但不保证一定能获取到在选代器创建后被删除的元素。


      弱一致性是指在并发操作中,不同线程之间的操作可能不会立即同步,但系统会在某个时刻趋于致。这种弱一致性的特性有助于实现 fail-safe 行为,即使在选代或操作过程中发生了并发修改,也不会导致异常或数据损坏。即他不会抛出 ConcurrentModifiedException


另外。在JDK 1.8 中,ConcurrentHashMap 中的 Segment 被移除了,取而代之的是使用类似于 castsynchronized 的机制来实现并发访问。在遍历ConcurrentHashMap时,只需要获取每个桶的头结点即可,因为每个桶的头结点是原子更新的,不会被其他线程修改。这个设计允许多个线程同时修改不同的桶,这减少了并发修改的概率,从而降低了冲突和数据不一致的可能性。


也就是说,ConcurrentHashMap 通过弱一致性选代器和 Segment 分离机制来实现 fail-safe 特性可以保证在遍历时不会受到其他线程修改的影响


ConcurrentHashMap 是一种线程安全的哈希表实现,它通过使用分段锁(Segmentation)和无锁(Lock-Free)技术来实现高并发性和线程安全性。下面我将详细解释 ConcurrentHashMap 是如何保证 fail-safe 的:


  1. 分段锁(Segmentation)

    • ConcurrentHashMap 将整个哈希表分成多个段(Segment),每个段都是一个独立的锁。当进行读操作或写操作时,只需要锁定对应的段,而不是整个哈希表。这大大减少了锁的竞争,提高了并发性能。
    • 每个段都是一个独立的哈希表,有自己的锁。当一个线程访问某个段时,会先对该段加锁,然后进行相应的操作。其他线程可以同时访问其他段,互不干扰。

  1. 无锁(Lock-Free)技术

    • ConcurrentHashMap 还使用了无锁技术来进一步优化性能。在某些情况下,不需要加锁就可以进行操作,比如使用 CAS(Compare-and-Swap)操作来更新节点的值。
    • CAS 是一种无锁的原子操作,它比较并交换内存中的值。如果值未被其他线程修改过,则成功更新;否则,该操作失败并重新尝试。通过使用 CAS 操作,ConcurrentHashMap 可以在不加锁的情况下实现线程安全。

  1. 数据结构优化

    • ConcurrentHashMap 使用了各种数据结构优化来提高性能和减少锁竞争。例如,使用红黑树来处理哈希冲突,以及使用链表、数组等数据结构来存储键值对
    • 这些优化使得 ConcurrentHashMap 在高并发环境下能够提供更好的性能和更低的锁竞争。

  1. 失败安全

    • ConcurrentHashMap 的设计目标是提供 fail-safe 的保证,即使在并发操作中出现异常,也能保证数据的一致性。
    • 由于使用了分段锁和无锁技术,ConcurrentHashMap 能够保证操作的原子性和不可变性。即使一个线程在执行操作时发生异常,也不会影响到其他线程的操作。其他线程仍然可以正常地读取和写入数据,不会导致数据的不一致性。
    • 同时,ConcurrentHashMap 还使用了回滚机制来处理异常情况。如果一个线程在执行操作时出现异常,该操作会被回滚,不会对其他线程造成影响。这种回滚机制保证了即使发生异常,也能保持数据的一致性。

ConcurrentHashMap 通过分段锁、无锁技术和数据结构优化等手段,实现了线程安全和 fail-safe 的保证。即使在并发操作中出现异常,也能保证数据的一致性,提供了高效的并发访问和数据处理能力。


来看一段代码Demo,帮助理论知识的理解:


import java.util.concurrent.ConcurrentHashMap;  
import java.util.concurrent.atomic.AtomicInteger;  /**
*   @author xinbaobaba
*   Demo展示了ConcurrentHashMap如何实现更高级的线程安全和"fail-safe"功能
*/public class ConcurrentHashMapAdvancedExample {  public static void main(String[] args) {  // 创建一个 ConcurrentHashMap 实例  ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap<>();  // 模拟多个线程同时访问和修改 map  Thread thread1 = new Thread(() -> {  for (int i = 0; i < 100; i++) {  map.put("A", new AtomicInteger(i));  System.out.println("Thread 1: " + map.get("A").get());  }  });  Thread thread2 = new Thread(() -> {  for (int i = 0; i < 100; i++) {  map.put("B", new AtomicInteger(i));  System.out.println("Thread 2: " + map.get("B").get());  }  });  thread1.start();  thread2.start();  }  
}

在这个Demo中,创建了一个 ConcurrentHashMap 实例 map,并使用两个线程 thread1 和 thread2 同时访问和修改这个映射。这次我们使用 AtomicInteger 作为映射的值类型,而不是简单的IntegerAtomicInteger是一个线程安全的整数类型,它提供了原子操作,可以在并发环境下安全地进行增加、减少等操作。

在每个线程中,我们使用一个循环来多次执行操作。每个线程都向映射中添加一个键值对,并使用get()方法获取并打印相应的值。由于ConcurrentHashMap的内部锁和分段锁机制,这些操作仍然是线程安全的。即使在并发环境下,也不会出现数据竞争或不一致的情况。同时,由于使用了AtomicInteger作为值类型,我们可以保证对整数值的操作是原子的,进一步增强了线程安全性。

即使一个线程在执行操作时出现异常,也不会影响其他线程的操作。每个线程操作的是不同的键,互不干扰。即使一个线程的操作被中断或回滚,也不会对其他线程造成影响,因为每个线程都有自己的数据副本,并且操作是原子的。这样就能保证在整个并发操作过程中的数据一致性和"fail-safe"


✅拓展知识仓


✅分段锁


分段锁是一种用于实现高并发性能的锁策略,通常在数据库和并发编程中使用。它将数据分成多个段,并对每个段加锁,允许多个线程同时访问不同的段,而不会相互阻塞。


分段锁的优点在于它能够提高并发性能,通过将数据分成多个段并使用锁来保护每个段,允许多个线程同时访问不同的段,而不会相互阻塞。这种策略能够减少锁的竞争,从而提高并发性能。另外,分段锁还可以通过细化锁的粒度来提高并发性能,使得在多线程环境下能够更好地利用系统资源。


分段锁的缺点在于实现较为复杂,需要处理多个段之间的同步和通信,增加了代码的复杂性和出错的可能性。另外,分段锁需要更多的锁资源,因为每个段都需要一个锁来保护。这可能会导致更多的锁竞争和上下文切换,从而降低性能。


分段锁是一种用于实现高并发性能的锁策略,通过将数据分成多个段并使用锁来保护每个段,允许多个线程同时访问不同的段,而不会相互阻塞。它能够提高并发性能,但实现较为复杂,需要谨慎处理多个段之间的同步和通信


看一个Demo,帮助理解分段锁:


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
/**
*  如何使用`java.util.concurrent.locks.ReentrantLock`和`java.util.concurrent.locks.Condition`来实现一个线程安全的计数器
*/
public class SafeCounter {private int count = 0;private Lock lock = new ReentrantLock();private Condition countCondition = lock.newCondition();public void increment() {lock.lock();try {while (count == Integer.MAX_VALUE) {try {countCondition.await(); // 等待其他线程重置count} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}}count++;System.out.println("Count: " + count);countCondition.signalAll(); // 通知其他等待的线程} finally {lock.unlock();}}public void decrement() {lock.lock();try {while (count == 0) {try {countCondition.await(); // 等待其他线程增加count} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}}count--;System.out.println("Count: " + count);countCondition.signalAll(); // 通知其他等待的线程} finally {lock.unlock();}}public int getCount() {return count;}
}

示例中,使用 ReentrantLockCondition来实现一个线程安全的计数器。我们使用了lock来保护共享变量count的访问,并使用Condition来协调线程之间的等待和通知。在increment()decrement()方法中,我们使用while循环来检查count的值,并在需要时使用await()方法等待其他线程的操作。一旦满足条件,我们使用signalAll()方法通知其他等待的线程。通过这种方式,我们确保了线程安全和数据的一致性。


☑️分段锁适用于什么情况


分段锁是一种用于实现高并发性能的锁策略,通常在缓存系统和并发容器等场景中使用。它将数据分成多个段,并对每个段加锁,允许多个线程同时访问不同的段,而不会相互阻塞。


分段锁适用于以下情况:


  1. 缓存系统:在缓存系统中,使用分段锁可以将缓存数据分为多个段,每个段使用独立的锁控制。这样,不同的线程可以同时读取或修改不同的缓存数据段,从而提高缓存访问的并发度和性能。

  1. 并发容器:并发容器是用于存储和管理多个数据项的容器,如集合、列表等。使用分段锁可以将容器中的数据分成多个段,并使用独立的锁来控制每个段。这样可以避免锁的竞争和锁的粒度过大导致的性能问题,从而提高容器的并发性能。

  1. 高性能数据库:在一些高性能数据库中,为了提高并发性能,可以使用分段锁来控制对数据的并发访问。通过将数据分成多个段并使用独立的锁来控制每个段,可以允许多个线程同时访问不同的数据段,从而提高数据库的并发性能。

  1. 路由器和操作系统:在一些并发访问场景中,如路由器和操作系统等,也可以使用分段锁来提高系统的并发度和性能。通过将数据分成多个段并使用独立的锁来控制每个段,可以避免锁的竞争和性能问题,从而提高系统的并发性能。

分段锁适用于需要高并发访问数据的情况,特别是在缓存系统、并发容器、高性能数据库、路由器和操作系统等场景中。通过将数据分成多个段并使用独立的锁来控制每个段,可以允许多个线程同时访问不同的数据段,从而提高并发性能。


🟡分段锁的锁争用情况,是否会带来一定的性能影响


分段锁的锁争用情况可能会导致一定的性能影响。当多个线程同时访问分段锁所保护的数据时,如果多个线程试图同时访问同一数据段,就会发生锁争用。此时,线程将会被阻塞,直到锁被释放。


锁争用会导致线程等待,增加了线程的上下文切换开销和CPU资源的消耗,从而可能降低程序的并发性能和响应速度。在高并发环境下,锁争用情况可能会导致更为显著的性能下降。


为了避免锁争用对性能的影响,可以采用一些策略和技术


  1. 优化数据分布:通过合理地分布数据,使得线程尽可能访问不同的数据段,从而减少锁争用的可能性。例如,可以使用散列函数来分配数据到不同的段。

  1. 减小锁粒度:通过将数据分成更小的段,可以减小锁的粒度,从而减少锁争用的可能性。例如,可以使用更小的数据结构或更精细的锁控制。

  1. 使用更高效的锁机制:例如,使用读写锁、自旋锁或乐观锁等更高效的锁机制,可以减少线程等待锁的时间和上下文切换的开销。

  1. 避免长时间持有锁:通过减少线程持有锁的时间,可以减少其他线程等待该锁的时间,从而减少锁争用的可能性。例如,尽可能地减少在持有锁时的计算和I/O操作。

  1. 使用缓存和缓冲技术:通过使用缓存和缓冲技术,可以减少对数据的直接访问,从而减少对分段锁的竞争。例如,可以使用缓存池或缓冲队列等技术来存储常用的数据项。

分段锁的锁争用情况可能会导致一定的性能影响。通过优化数据分布、减小锁粒度、使用更高效的锁机制、避免长时间持有锁以及使用缓存和缓冲技术等策略和技术,可以减少锁争用的可能性并提高程序的并发性能。


✔️分段锁的优缺点


分段锁优点

  1. 高并发性:允许并发访问不同段的数据,提高了并发性能。通过将数据分成多个段,并使用独立的锁控制每个段,允许多个线程同时访问不同的数据段,从而减少了线程之间的竞争和阻塞。
  2. 细粒度控制:提供更精细的锁控制,提高了系统的灵活性和可扩展性。与全局锁或较大粒度的锁相比,分段锁可以将锁的粒度减小,从而更好地满足不同场景的需求。
  3. 减少锁竞争:如果数据分布均匀,每个锁控制的数据区域不会太大,从而减少了锁的竞争。通过合理地分布数据到不同的段,可以降低线程对同一数据段的竞争,从而提高并发性能。

分段锁缺点

  1. 内存碎片化:将数据分成多个段可能会导致内存碎片化。每个段可能需要独立的内存空间,并且可能需要非连续的内存分配,这可能会导致内存碎片和空间浪费。
  2. 锁竞争概率低:当数据分布不均匀时,某些段上的锁竞争概率可能会很高,导致性能下降。尽管可以通过合理地分布数据来减少这种情况的发生,但在极端情况下仍然可能存在锁竞争的问题。
  3. 实现复杂性:分段锁需要维护多个锁的控制和管理,增加了实现的复杂性和潜在的错误风险。需要确保正确地管理每个段的锁定和解锁操作,以避免出现死锁或竞态条件等问题。
  4. 扩展性问题:分段锁的数量是固定的,这可能导致在某些情况下难以扩展。如果需要更多的段来容纳更多的数据或线程,可能需要重新设计整个锁机制。

🟢 还有哪些其他的线程安全哈希表实现


除了 ConcurrentHashMap,还有其他几种线程安全的哈希表实现。其中一种是 Hashtable,另一种是 Collections.synchronizedMap。然而,这些实现方法都存在性能问题,因为它们在多线程环境下容易造成锁竞争,导致性能下降。


具体来说,Hashtable 是早期 Java 中的线程安全哈希表实现,它使用一个内部锁来保护整个哈希表的操作。这意味着在多线程环境下,只有一个线程能够访问哈希表,其他线程必须等待锁释放。因此,Hashtable 的性能在竞争激烈的场景下较差。


另外,Collections.synchronizedMap 方法提供了一个线程安全的包装器,可以用于将普通的 Map 转换为线程安全的版本。然而,它也是对整个哈希表进行加锁,因此在多线程环境下性能同样较差。


相比之下,ConcurrentHashMap 的设计更加先进,它通过分段锁技术将数据分成多个段,并对每个段进行加锁。这样可以在多线程环境下同时访问不同的段,提高了并发性能。因此,在需要线程安全的哈希表时,推荐使用 ConcurrentHashMap


🟠Hashtable和 Collections区别


Hashtable和Collections的主要区别在于同步性、历史背景以及使用场景。

  1. 同步性:Hashtable是线程安全的,所有的公共方法都有synchronized关键字,这意味着在多线程环境下,Hashtable是安全的。而Collections则提供了一系列帮助方法,如排序、搜索、线程安全化等,但它本身并不是线程安全的。

  1. 历史背景:Hashtable是基于陈旧的Dictionary类,它在Java 1.1中就已经存在。而Collections类是Java 1.2中引入的,它为集合类提供了一系列静态方法。

  1. 使用场景:如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap,因为它在效率上可能更高。而Collections主要用于对集合类进行各种操作,如排序、搜索等。

Hashtable和Collections在同步性、历史背景和使用场景上存在明显的差异。需要根据具体的使用场景和需求来选择使用哪种方式


看一下具体的使用Hashtable和Collections的示例:


使用Hashtable的示例:

import java.util.Hashtable;public class HashtableExample {public static void main(String[] args) {Hashtable<String, String> hst = new Hashtable<>();String sid = "SID1";String name = "Name1";hst.put(sid, name);System.out.println("添加成功,是否继续添加? (Y/N)");String temp = Console.ReadLine();if (temp == "Y" || temp == "y") {continue;} else {break;}// 输出键为奇数的元素System.out.println("奇数键的元素:");for (var iteminhst : hst) {if (iteminhst.Key % 2 == 1) {System.out.println("{0}, {1}", iteminhst.Value, iteminhst.Key);}}}
}

使用Collections的示例:


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;public class CollectionsExample {public static void main(String[] args) {List<String> list = new ArrayList<>();Scanner scanner = new Scanner(System.in);while (true) {System.out.println("请输入一个字符串(输入exit退出):");String input = scanner.nextLine();if (input.equalsIgnoreCase("exit")) {break;} else {list.add(input);}// 对列表进行排序并输出Collections.sort(list);System.out.println("排序后的列表:");for (String str : list) {System.out.println(str);}}}
}

🟡分段锁和锁之间的区别是什么


分段锁和锁之间的区别:


分段锁是ConcurrentHashMap中使用的锁策略,实质是一种锁的设计策略,并不是指某一个特定的锁。它不是对整个数据结构加锁,而是通过hashcode来判断该元素应该存放的分段,然后对这个分段进行加锁。这种策略的优点在于细化锁的粒度,提高并发性能。当多个线程同时访问不同的分段时,可以实现真正的并行插入,提高并发性能。而当统计size时,需要获取所有的分段锁才能统计。


而锁则是指用于保证线程安全的机制,通过加锁来防止多个线程同时访问某一共享资源,以避免数据不一致的问题。常见的锁有公平锁和非公平锁。公平锁按照申请锁的顺序来获取锁,而非公平锁则不是按照申请锁的顺序来获取锁,其优点在于吞吐量比公平锁大。


分段锁是一种设计策略,通过对数据进行分段并加锁来提高并发性能;而锁则是一种保证线程安全的机制,通过加锁来避免数据不一致的问题


🟢分段锁比锁更加安全吗


分段锁和锁都是线程安全哈希表实现中使用的技术,它们各自具有不同的优缺点,无法简单地判断哪个更加安全。


分段锁的优点在于它能够提高并发性能,通过将数据分成多个段并使用锁来保护每个段,允许多个线程同时访问不同的段,而不会相互阻塞。这种策略能够减少锁的竞争,从而提高并发性能。另外,分段锁还可以通过细化锁的粒度来提高并发性能,使得在多线程环境下能够更好地利用系统资源。


然而,分段锁也存在一些缺点。首先,它需要更多的锁,因为每个段都需要一个锁来保护。这可能会导致更多的锁竞争和上下文切换,从而降低性能。其次,分段锁的实现较为复杂,需要处理多个段之间的同步和通信,增加了代码的复杂性和出错的可能性。


锁的优点在于实现简单,使用方便。通过加锁来保证线程安全,可以避免多个线程同时访问共享资源,从而避免数据不一致的问题。但是,锁的缺点在于它可能会降低并发性能,因为多个线程访问共享资源时需要等待获取锁。如果锁的粒度较大,可能会导致大量的锁竞争和上下文切换,从而降低系统的吞吐量。


分段锁和锁都是线程安全哈希表实现中使用的技术,它们各有优缺点。选择使用分段锁还是锁取决于具体的应用场景和需求,需要根据实际情况进行权衡和选择。


🟢弱一致性保障


ConcurrentHashMap 提供的是弱一致性保障,这是因为在多线程并发修改 ConcurrentHashMap时,可能会出现一些短暂的不一致状态,即一个线程进行了修改操作,但是另一个线程还没有看到这个修改。因此,在并发修改 ConcurrentHashMap 时,不能保证在所有时刻 ConcurrentHashMap 的状态都是一致的。


首先就是因为前面提到的弱一致性迭代器,在 ConcurrentHashMap 中,使用选代器遍历时,不能保证在迭代器创建后所有的元素都被迭代器遍历到。这是因为在迭代器遍历过程中,其他线程可能会对ConcurrentHashMap 进行修改,导致迭代器遍历的元素发生变化。为了解决这人问题ConcurrentHashMap 提供了一种弱一致性选代器,可以获取到在迭代器创建后被添加到ConcurrentHashMap 中的元素,但是可能会获取到迭代器创建后被删除的元素。

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

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

相关文章

Python从入门到网络爬虫(控制语句详解)

前言 做任何事情都要遵循一定的原则。例如&#xff0c;到图书馆去借书&#xff0c;就需要有借书证&#xff0c;并且借书证不能过期&#xff0c;这两个条件缺一不可。程序设计亦是如此&#xff0c;需要使用流程控制实现与用户的交流&#xff0c;并根据用户需求决定程序“做什么…

如何实现两台Linux虚拟机ssh免密登录

实验开始前 1.准备好两台虚拟机&#xff08;下载好镜像文件的&#xff09; 2.实验步骤 公钥验证&#xff1a;&#xff08;免密登陆验证方式&#xff09; &#xff08;1&#xff09;生成非对称秘钥 [rootclient ~]# ssh-keygen -t rsa Generating public/private rsa key pai…

2024华数杯国际大学生数学建模思路+模型+代码+论文

2024年第二届“华数杯”国际大学生数学建模竞赛 赛题思路模型代码论文&#xff1a;开赛后第一时间更新&#xff0c;文末名片获取 竞赛信息 一、比赛背景 2024年第二届“华数杯”国际大学生数学建模竞赛&#xff08;以下简称“竞赛”&#xff09;是天津市未来与预测科学研究会…

秒变办公达人,只因用了这5款在线协同文档app!

在日常工作中&#xff0c;我们不可避免地需要处理各种文档&#xff0c;有时你可能会为如何高效地管理这些文档而感到烦恼&#xff0c;或是不知道如何挑选合适的在线文档工具&#xff1f; 不用担心&#xff01;在这篇文章中&#xff0c;我们将介绍5个好用的在线文档工具App&…

SpringBoot+Hutool实现图片验证码

图片验证码在注册、登录、交易、交互等各类场景中都发挥着巨大作用&#xff0c;能够防止操作者利用机器进行暴力破解、恶意注册、滥用服务、批量化操作和自动发布等行为。 创建一个实体类封装&#xff0c;给前端返回的验证码数据&#xff1a; Data public class ValidateCodeV…

一、数据结构基本概念

数据结构基本概念 一、数据结构基本概念1.基本概念和术语1.1数据&#xff08;Data&#xff09;1.2 数据元素&#xff08;Data element&#xff09;1.3 数据项 &#xff08;Data Item&#xff09;1.4 数据对象 &#xff08;Data Object&#xff09;1.5 数据结构 &#xff08;Dat…

基于 Validator 类实现 ParamValidator,用于校验函数参数

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、代码实现3、测试代码4、日志输出 三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器&#xff0c;从编码到发布全过程》 2、相关回顾 使用 TypeV…

Every Nobody Is Somebody 「每小人物都能成大事」

周星驰 NFT Nobody即将发售&#xff0c;Nobody共创平台 Every Nobody Is Somebody Nobody 关于Nobody&#xff1a;Nobody是一款Web3共创平台&#xff0c;旨在为创作者提供一个交流和合作的场所&#xff0c;促进创意的产生和共享。通过该平台&#xff0c;创作者可以展示自己的作…

git秘钥过期 ERROR: Your SSH key has expired

文章目录 1、错误提示Your SSH key has expired2、登录Github确认3、重新设置秘钥 1、错误提示Your SSH key has expired 使用git命令时遇到Github 的 SSH Key秘钥过期&#xff0c;提示错误ERROR: Your SSH key has expired 2、登录Github确认 首先登录Github查看&#xff…

L1-015 跟奥巴马一起画方块(Java)

美国总统奥巴马不仅呼吁所有人都学习编程&#xff0c;甚至以身作则编写代码&#xff0c;成为美国历史上首位编写计算机代码的总统。2014年底&#xff0c;为庆祝“计算机科学教育周”正式启动&#xff0c;奥巴马编写了很简单的计算机代码&#xff1a;在屏幕上画一个正方形。现在…

某查查请求头参数加密分析(含JS加密算法与Python爬虫源码)

文章目录 1. 写在前面2. 请求分析3. 断点分析4. 扣加密JS5. Python爬虫代码实现 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff…

基于SELinux三权分立配置方法

1.系统安装 系统安装完成后,系统当前的SELinux配置为: # cat /etc/selinux/config SELINUX=enforcing SELINUXTYPE=targeted 2.SELinux环境准备 # yum install setools policycoreutils.x86_64 selinux-policy-mls.noarch setroubleshoot.x86_64 setools-console -y 3.SELin…

嵌入式c语言学习笔记:可重入函数与不可重入函数

什么是可重入函数与不可重入函数&#xff1f; 在一个多任务环境中&#xff0c;一个函数如果可以被多次重复调用&#xff0c;或者被多个任务并发调用&#xff0c;函数在运行过程中可以随时随地被打断&#xff0c;并不影响该函数的运行结果&#xff0c;我们称这样的函数为可重入…

Redis 常用的数据类型及用法

1. 字符串&#xff08;Strings&#xff09; 字符串是最基本的 Redis 数据类型。它可以包含任何形式的数据&#xff0c;比如文本、数字或二进制数据。 基本用法&#xff1a; 设置值: SET key value获取值: GET key删除键: DEL key自增: INCR key追加值: APPEND key value 示…

vue element plus TimePicker 时间选择器

用于选择或输入日期 TIP 在 SSR 场景下&#xff0c;您需要将组件包裹在 <client-only></client-only> 之中 (如: Nuxt) 和 SSG (e.g: VitePress). 任意时间点# 可以选择任意时间 提供了两种交互方式&#xff1a;默认情况下通过鼠标滚轮进行选择&#xff0c;打开…

三、java线性表(顺序表、链表、栈、队列)

java线性表 三、线性表1.1 顺序表1.2 链表1.2.1 单向链表&#xff08;Singly Linked List&#xff09;1.2.2 双向链表&#xff08;Doubly Linked List&#xff09; 1.3 LinkedList VS ArrayList1.3.7 使用 LinkedList 的场景 1.4 栈1.5 队列 三、线性表 线性表是一种经典的数据…

LeetCode2696. Minimum String Length After Removing Substrings

文章目录 一、题目二、题解 一、题目 You are given a string s consisting only of uppercase English letters. You can apply some operations to this string where, in one operation, you can remove any occurrence of one of the substrings “AB” or “CD” from s…

手撕单链表(单向,不循环,不带头结点)的基本操作

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

mercury靶机

文章妙语 不与伪君子争名&#xff0c;不与真小人争利&#xff0c;不与执拗人争理&#xff0c;不与匹夫争勇&#xff0c;不与酸儒争才。不与蠢人施恩 一、信息收集 主机探测 端口探测 探测主机详细版本信息 8080开了http服务 目录扫描 robots.txt目录下什么也没有 二&#xff0…

Python | Iter/genartor | 一文了解迭代器、生成器的含义\区别\优缺点

前提 一种技术的出现&#xff0c;需要考虑&#xff1a; 为了实现什么样的需求&#xff1b;遇到了什么样的问题&#xff1b;采用了什么样的方案&#xff1b;最终接近或达到了预期的效果。 概念 提前理解几个概念&#xff1a; 迭代 我们经常听到产品迭代、技术迭代、功能迭代…