【昕宝爸爸定制】如何将集合变成线程安全的?

在这里插入图片描述

如何将集合变成线程安全的?

  • ✅典型解析
  • 🟢拓展知识仓
    • ☑️Java中都有哪些线程安全的集合?
    • 🟠线程安全集合类的优缺点是什么
    • 🟡如何选择合适的线程安全集合类
    • ☑️如何解决线程安全集合类并发冲突问题
      • ✔️乐观锁实现方式 (具体步骤)。
      • ✅乐观锁在数据量大的情况下性能如何
      • ✅乐观锁的优缺点
  • ✅ 什么是写时复制?
    • ✅ 什么是COW,如何保证的线程安全?


✅典型解析


1 . 在调用集合前,使用synchronized或者 ReentrantLock 对代码加锁 (读写都要加锁)


public class SynchronizedCollectionExample {private List<Integer> list = new ArrayList<>();public void add(int value) {synchronized (SynchronizedCollectionExample.class) {list.add(value);}}public int get(int index) {synchronized (SynchronizedCollectionExample.class) {return list.get(index);}}
}

2 . 使用 ThreadLocal ,将集合放到线程内访问,但是这样集合中的值就不能被其他线程访问了


public class ThreadlocalCollectionExample {private ThreadLocal<list<Integer>> threadlocallist = Threadlocal.withInitial(Arraylist::new);public void add(int value) {threadLocalList.get( ) .add(value);}public int get(int index) {return threadLocalList.get().get(index);}
}

3 . 使用Collections.synchronizedXXX()方法,可以获得一个线程安全的集合


import java.util.Collections;
import java.util.List;
import java.util.ArrayList;public class CollectionssynchronizedExample {List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<Integer>());public void add(int value) {synchronizedList.add(value);}	public int get(int index) {return synchronizedList.get(index);}
}

4 . 使用不可变集合进行封装,当集合是不可变的时候,自然是线程安全的


import com.google.common.collect.ImmutableList;public class ImmutableCollectionExample {private Immutablelist<Integer> immutablelist = Immutablelist.of(123);public int get(int index) {return immutablelist .get(index);}	
}

或者(or)


import java.util.List;public class ImmutableJava9Example {private List<Integer> immutableList = List.of(1,23);public int get(int index) {return immutablelist.get(index);}
}

🟢拓展知识仓


☑️Java中都有哪些线程安全的集合?


Java1.5并发包 (java.util.concurrent) 包含线程安全集合类,允许在迭代时修改集合。


1. ConcurrentHashMap

2.ConcurrentLinkedDeque

3. ConcurrentLinkedQueue

4.ConcurrentSkipListMap

5. ConcurrentSkipSet

6.CopyOnWriteArrayList

7.CopyOnWriteArraySet


🟠线程安全集合类的优缺点是什么


线程安全集合类的优缺点如下:

优点

1. 线程安全:线程安全集合类可以在多线程环境中安全地使用,不会出现数据不一致或竞争条件的问题

2. 高性能:线程安全集合类在处理大量数据时通常具有较好的性能,因为它们内部进行了优化以减少同步的开销

3. 可靠性:由于线程安全集合类具有内置的同步机制,因此它们可以避免因并发问题而导致的数据损坏或程序崩溃

缺点

1. 资源竞争:由于线程安全集合类的同步机制,当多个线程同时访问这些集合类时,会导致资源竞争,从而影响程序的性能

2. 死锁风险:线程安全集合类可能会增加死锁的风险,尤其是在复杂的并发环境中。

3. 过度同步:线程安全集合类的同步机制可能会导致过度同步,从而限制了并发性能的进一步提高。

4. 性能调优难度:由于线程安全集合类的内部实现较为复杂,因此对它们的性能调优可能会更加困难。


线程安全集合类在多线程环境中是必不可少的,但使用时需要注意它们的优缺点,并根据实际需求进行选择和调整。


我们来看一个场景的Demo:


import java.util.concurrent.*; 
/**
* @author xinbaobaba
* 涉及多个线程和多个线程安全集合类
* 这个例子使用 java.util.concurrent 包中的 ExecutorService 
* 和 BlockingQueue 来实现一个线程安全的生产者-消费者模型
*/    
public class ProducerConsumerExample {  // 定义最大队列大小  private static final int MAX_QUEUE_SIZE = 10;  // 定义线程安全的阻塞队列  private static final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE);  public static void main(String[] args) {  // 创建一个固定大小的线程池  ExecutorService executorService = Executors.newFixedThreadPool(2);  // 创建生产者线程  Producer producer = new Producer(queue);  // 创建消费者线程  Consumer consumer = new Consumer(queue);  // 将生产者线程提交到线程池中运行  executorService.submit(producer);  // 将消费者线程提交到线程池中运行  executorService.submit(consumer);  }  // 生产者线程内部类  static class Producer implements Runnable {  private final BlockingQueue<Integer> queue; // 生产者使用的队列  public Producer(BlockingQueue<Integer> queue) {  this.queue = queue; // 初始化队列  }  @Override  public void run() { // 生产者的运行方法  try {  for (int i = 0; i < 100; i++) { // 循环生产100个元素  queue.put(i); // 将元素放入队列中,如果队列满则阻塞等待空间可用  System.out.println("Produced: " + i); // 打印已生产的元素  Thread.sleep(100); // 生产一个元素后休眠100毫秒,模拟生产时间  }  } catch (InterruptedException e) { // 如果生产过程中被中断,则捕获异常并打印堆栈信息  e.printStackTrace();  }  }  }  // 消费者线程内部类  static class Consumer implements Runnable {  private final BlockingQueue<Integer> queue; // 消费者使用的队列  public Consumer(BlockingQueue<Integer> queue) { // 初始化队列  this.queue = queue;  }  @Override  public void run() { // 消费者的运行方法  try {  while (true) { // 无限循环消费元素,直到程序被终止或异常发生  Integer value = queue.take(); // 从队列中取出元素,如果队列空则阻塞等待有元素可用  System.out.println("Consumed: " + value); // 打印已消费的元素  }  } catch (InterruptedException e) { // 如果消费过程中被中断,则捕获异常并打印堆栈信息  e.printStackTrace();  }  }  }  
}

Demo中,创建一个生产者和一个消费者线程。生产者线程将数字0到99放入队列中,而消费者线程从队列中取出数字并打印出来。我们使用 BlockingQueue 来实现线程安全的队列,它提供了 put() take()方法,这些方法在队列满或队列空时会自动阻塞,直到队列不再满或不再空为止。这样就可以保证生产者和消费者线程在访问队列时不会发生并发冲突


🟡如何选择合适的线程安全集合类


选择合适的线程安全集合类需要考虑以下几个方面:

  1. 并发级别:首先需要了解应用程序的并发级别,即同时访问集合的线程数量。高并发级别需要选择性能更好的线程安全集合类,如 ConcurrentHashMap
  2. 读写比例:根据应用程序对读和写的需求,选择适合的线程安全集合类。如果读操作远多于写操作,则可以选择 CopyOnWriteArrayListCopyOnWriteArraySet,它们在读操作时不需要同步,而在写操作时复制底层数组,保证了线程安全。
  3. 锁粒度:不同的线程安全集合类使用的锁粒度不同,这会影响并发性能。例如,Hashtable 使用单个锁保护整个表,而 ConcurrentHashMap 使用多个锁保护不同的段,从而提供更好的并发性能。
  4. 元素访问顺序:根据对元素访问顺序的需求,选择具有合适迭代器的线程安全集合类。例如,VectorHashtable 提供了稳定的迭代器,而 ConcurrentHashMap 不保证迭代顺序。
  5. 内存效率:线程安全集合类的内存效率也是一个重要的考虑因素。例如,HashtableVector 在添加元素时需要扩容,这可能会导致额外的内存开销。
  6. 自定义需求:根据应用程序的特定需求,可以选择具有自定义功能的线程安全集合类。例如,如果需要自定义的锁策略或特殊的数据结构,可以自行实现线程安全的集合类。

选择合适的线程安全集合类需要综合考虑应用程序的并发级别、读写比例、锁粒度、元素访问顺序、内存效率和自定义需求等因素。在选择时,可以参考Java标准库提供的线程安全集合类,并根据实际情况进行选择和调整。


☑️如何解决线程安全集合类并发冲突问题


解决线程安全集合类并发冲突问题可以采用以下几种方法:

  1. 使用锁:在访问线程安全集合类时,可以使用适当的锁机制来避免并发冲突。例如,可以使用 synchronized 关键字或ReentrantLock 来实现同步访问。

  2. 使用并发集合类:Java标准库提供了一些并发集合类,如ConcurrentHashMapCopyOnWriteArrayList等,这些集合类内部已经实现了线程安全,可以避免并发冲突。

  3. 使用乐观锁:乐观锁采用乐观策略,即在读取数据时不加锁,在更新数据时通过版本号或CAS(Compare and Swap)机制来保证数据的一致性。乐观锁可以减少锁的竞争,提高并发性能。

  4. 使用读写锁:读写锁允许多个线程同时读取数据,但在写入数据时需要独占式的访问。Java标准库中的ReadWriteLock接口提供了读写锁机制。

  5. 使用分段锁:分段锁是将数据分成多个段,每个段使用独立的锁来保护。这样可以减少锁的竞争范围,提高并发性能。例如,ConcurrentHashMap就是使用分段锁实现的。

  6. 使用无锁机制:无锁机制是一种基于CAS操作的并发控制方法,它不需要显式的锁机制即可实现线程安全。无锁机制的优点是避免了锁的竞争和死锁问题,但实现起来较为复杂。

解决线程安全集合类并发冲突问题需要根据实际情况选择合适的并发控制方法。在选择时,需要考虑应用程序的并发级别、读写比例、数据一致性需求等因素,并权衡性能、可读性和可维护性等方面


✔️乐观锁实现方式 (具体步骤)。


乐观锁是一种并发控制策略,它假设多个事务在同一时间对同一数据进行操作时,不会产生冲突,只有在提交更新时才会检查是否有冲突。如果发现冲突,则回滚事务。乐观锁的实现方式主要有两种:数据版本记录机制和基于CAS操作的机制。

数据版本记录机制是乐观锁最常用的实现方式。具体步骤如下:

  1. 将需要锁定的资源以键值对的方式存储在数据库中。
  2. 给资源添加一个版本号字段,用于记录资源的版本信息。
  3. 当需要访问资源时,先读取资源的当前版本号,并将该版本号保存在本地。
  4. 处理完需要访问的数据后,将本地保存的版本号与数据库中的版本号进行比较。如果两个版本号相等,则提交事务;否则,表示资源已经被其他事务修改,需要回滚事务并重新处理。

基于CAS操作的机制是一种无锁机制,它通过比较并交换(Compare-And-Swap, CAS)操作来实现并发控制。具体步骤如下:

  1. 将需要锁定的资源以键值对的方式存储在内存中。
  2. 当需要更新资源时,使用CAS操作来比较并交换资源的新旧值。如果资源没有被其他线程修改过,则CAS操作成功,更新资源并提交事务;否则,表示资源已经被其他线程修改过,需要回滚事务并重新处理。

注意:乐观锁在处理大量并发请求时可能会有较高的回滚率,因此需要根据实际情况选择是否使用乐观锁,或者采用其他并发控制策略。


乐观锁的实现方式有很多种,具体选择哪种方式还需要根据实际业务场景和技术栈来决定。


✅乐观锁在数据量大的情况下性能如何


乐观锁在数据量大的情况下,性能表现主要取决于具体实现方式数据访问模式

乐观锁机制不会像悲观锁一样在操作系统中挂起,而是允许失败的线程重试,也允许自动放弃退出操作。因此,乐观锁相比悲观锁来说,不会带来死锁、饥饿等活性故障问题,线程间的相互影响也远远比悲观锁要小。


在乐观锁的实现方式中,使用数据版本(Version)记录机制是比较常用的一种。当大量数据需要更新时,每次更新都需要判断版本号是否匹配,这会增加一定的开销。但是,由于乐观锁避免了长事务中的数据库加锁开销,所以在高并发的情况下,乐观锁的性能表现可能优于悲观锁。

对于使用Redis实现乐观锁的情况,如果数据量非常大,频繁地读取和比较版本号可能会对性能产生一定的影响。但是,通过合理的设计和优化,比如使用有序集合(sorted set)来存储版本号,可以有效地提高性能。

乐观锁在数据量大的情况下,性能表现取决于具体实现方式和数据访问模式。通过合理的设计和优化,乐观锁可以提供较好的性能和并发控制能力。


乐观锁的核心思想是:在数据读取时,不对数据进行加锁,但在数据提交更新时,会判断在此期间是否有其他线程对数据进行了修改。下面是一个使用Java实现的乐观锁示例:


import java.util.concurrent.atomic.AtomicInteger;  public class OptimisticLocker {  // 定义一个版本号字段  private AtomicInteger version = new AtomicInteger(0);  // 获取当前版本号  public int getVersion() {  return version.get();  }  // 更新数据,并递增版本号  public boolean updateData(int newData, int expectedVersion) {  while (true) {  int currentVersion = version.get();  if (currentVersion != expectedVersion) {  // 如果当前版本号与期望的版本号不一致,说明数据在此期间被其他线程修改过  return false; // 返回false表示更新失败  } else {  // 更新数据并递增版本号  if (version.compareAndSet(currentVersion, currentVersion + 1)) {  // 数据更新成功,返回true表示更新成功  return true;  } else {  // 如果在更新过程中,其他线程也修改了版本号,则重新获取当前版本号并继续尝试更新  continue;  }  }  }  }  
}

可以看到:我们使用 AtomicInteger 作为版本号的存储,利用其原子性来保证并发访问时的正确性。updateData方法 接受两个参数:newData表示要更新的新数据,expectedVersion表示期望的版本号。方法中的 while循环 是为了确保数据的一致性。如果在更新过程中发现当前版本号与期望的版本号不一致,则认为数据被其他线程修改过,更新失败;否则,更新数据并递增版本号。


✅乐观锁的优缺点


乐观锁的优点主要体现在以下几个方面:

  1. 提高并发性能:乐观锁机制不会对读操作造成阻塞,只有在提交更新时才会进行版本检查,因此可以最大程度地提高并发性能,允许多个用户同时读取同一份数据,不会出现读阻塞的情况。
  1. 减少锁冲突:乐观锁采用版本号机制,可以降低锁冲突的概率,提高并发性能。
  1. 解决数据一致性问题:乐观锁通过版本检查的方式来解决并发修改可能导致的数据一致性问题。在提交更新时,会再次检查版本号是否一致,如果不一致则说明有其他用户修改了数据,避免了数据被覆盖或丢失的情况。
  1. 灵活性:乐观锁相对于悲观锁来说更加灵活,不需要给整个事务加锁,只在提交更新时进行版本检查。这样可以避免长时间的锁等待,提高系统的响应速度。

乐观锁缺点:


  1. 更新失败的概率较高:一旦出现锁冲突,可能会导致更新失败。如果锁的粒度掌握不好,可能会出现大量更新失败的情况,进而影响业务的正常运行。
  1. 适用场景有限:乐观锁适用于读操作频繁的场景,可以提高系统的吞吐量。但在写操作频繁的场景下,悲观锁可能更加适合,因为它能避免写操作的阻塞。
  1. 可能引发业务失败:由于乐观锁在更新时进行版本检查,一旦出现版本不一致的情况,可能会导致业务失败。这需要开发者在编写业务逻辑时充分考虑这种情况,并进行相应的处理。

总结:乐观锁的优点是可以提高并发性能、减少锁冲突、解决数据一致性问题以及提供更高的灵活性;而缺点是更新失败的概率较高、适用场景有限以及可能引发业务失败。在实际应用中,需要根据具体场景和需求来选择使用乐观锁还是悲观锁


## ✅如何解决线程安全缓存集合类并发冲突问题

线程安全缓存集合类并发冲突问题可以通过以下几种方式解决:


  1. 使用同步机制:可以使用synchronized关键字或Lock接口来实现同步机制,确保同一时间只有一个线程可以访问集合类,从而避免并发冲突。

  1. 使用并发集合类:Java提供了多种并发集合类,如ConcurrentHashMap、CopyOnWriteArrayList等,这些集合类在多线程环境下表现出较好的性能和线程安全性。

  1. 使用乐观锁:乐观锁通过版本号机制来解决并发修改可能导致的数据一致性问题。在提交更新时,会再次检查版本号是否一致,如果不一致则说明有其他用户修改了数据,避免了数据被覆盖或丢失的情况。

  1. 使用读写锁:读写锁允许多个线程同时读取数据,但在写入数据时需要独占式访问,这样可以减少锁的竞争,提高并发性能。

  1. 使用分布式缓存系统:分布式缓存系统可以将数据分散到多个节点上,每个节点都有自己的锁机制,从而避免了单点故障和并发冲突问题。

解决线程安全缓存集合类并发冲突问题需要根据具体场景和需求来选择适合的解决方案。


Demo:


import java.util.concurrent.ConcurrentHashMap;  
import java.util.concurrent.atomic.AtomicInteger;  /**
*  @author xinbaobaba
*  如何使用乐观锁来解决线程安全缓存集合类的并发冲突问题
*/  
public class ThreadSafeCache<K, V> {  private final ConcurrentHashMap<K, V> cache;  private final AtomicInteger version;  public ThreadSafeCache() {  cache = new ConcurrentHashMap<>();  version = new AtomicInteger(0);  }  public V get(K key) {  return cache.get(key);  }  public void put(K key, V value) {  while (true) {  int currentVersion = version.get();  if (currentVersion != 0) {  // 如果当前版本号不为0,说明有其他线程正在更新数据,需要重新尝试更新  continue;  } else {  // 更新数据并递增版本号  if (version.compareAndSet(currentVersion, currentVersion + 1)) {  V oldValue = cache.putIfAbsent(key, value);  if (oldValue == null) {  // 如果缓存中原本没有该键,则更新成功,返回  return;  } else {  // 如果缓存中原本已经有该键,则表示其他线程已经更新了数据,需要重新尝试更新  version.compareAndSet(currentVersion + 1, currentVersion + 2); // 增加版本号以表示冲突  continue; // 重新尝试更新  }  } else {  // 如果在更新过程中,其他线程也修改了版本号,则重新获取当前版本号并继续尝试更新  continue;  }  }  }  }  
}

示例中,使用ConcurrentHashMap作为缓存的存储,它提供了线程安全的并发访问。我们使用AtomicInteger作为版本号的存储,并利用其原子性来保证并发访问时的正确性。在put方法中,我们使用while循环来不断尝试更新数据,直到成功为止。在每次尝试更新之前,我们首先获取当前版本号,如果版本号不为0,则说明有其他线程正在更新数据,需要重新尝试更新。如果版本号为0,则表示当前没有其他线程正在更新数据,可以进行更新操作。在更新数据时,我们递增版本号,并使用compareAndSet方法来原子性地更新版本号和缓存数据。如果更新成功,则进一步判断缓存中原本是否已经有该键。如果缓存中原本没有该键,则表示更新成功,返回。如果缓存中原本已经有该键,则表示其他线程已经更新了数据,增加版本号以表示冲突,并重新尝试更新。这样就可以利用乐观锁的机制来解决线程安全缓存集合类的并发冲突问题。


✅ 什么是写时复制?


✅ 什么是COW,如何保证的线程安全?


Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略


从DK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是 CopyOnWriteArrayListCopyOnWriteArraySetCopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。


CopyOnWriteArrayList 相当于线程安全的ArrayListCopyOnWriteArrayList 使用了一种叫写时复制的方法,当有新元素addCopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后再将原来的数组引用指向到新数组。


这样做的好处是我们可以对 CopyOnWrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以 CopyOnWrite 容器也是一种读写分离的思想,读和写不同的容器。


注意: CopyOnWriteArrayList的整个add操作都是在锁的保护下进行的。也就是说add方法是线程安全的


CopyOnWrite并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新场景。


和ArrayList不同的是,它具有以下特性:


支持高效率并发且是线程安全


因为通常需要复制整个基础数组,所以可变操作 (add()、set() 和 remove() 等等) 的开销很大。

选代器支持hasNext() ,next() 等不可变操作,但不支持可变 remove() 等操作

使用迭代器进行遍历的速度很快,并目不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照

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

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

相关文章

Kubernetes实战(十五)-Pod垂直自动伸缩VPA实战

1 介绍 VPA 全称 Vertical Pod Autoscaler&#xff0c;即垂直 Pod 自动扩缩容&#xff0c;它根据容器资源使用率自动设置 CPU 和 内存 的requests&#xff0c;从而允许在节点上进行适当的调度&#xff0c;以便为每个 Pod 提供适当的资源。 它既可以缩小过度请求资源的容器&…

Oracle regexp_substr

select regexp_substr(123|456|789, [^|], 1, 2) from dual;

C练习——N个水手分椰子

题目&#xff1a; 五个水手在岛上发现一堆椰子&#xff0c;先由第1个水手把椰子分为等量的5堆&#xff0c;还剩下1个给了猴子&#xff0c;自己藏起1堆。然后&#xff0c;第2个水手把剩下的4堆混合后重新分为等量的5堆&#xff0c;还剩下1个给了猴子&#xff0c;自己藏起1堆。以…

uniapp 解决安卓App使用uni.requestPayment实现沙箱环境支付宝支付报错

背景&#xff1a;uniapp与Java实现的安卓端app支付宝支付&#xff0c;本想先在沙箱测试环境测支付&#xff0c;但一直提示“商家订单参数异常&#xff0c;请重新发起付款。”&#xff0c;接着报错信息就是&#xff1a;{ "errMsg": "requestPayment:fail [pa…

常用注解/代码解释(仅个人使用)

目录 第一章、代码解释①trim() 方法以及(Arrays.asList(str.split(reg)));②查询字典项②构建后端镜像shell命令解释 第二章、注解解释①PropertySource注解与Configurationproperties注解的区别 第三章、小知识①Linux系统中使用$符号表示变量 友情提醒: 先看文章目录&#…

设计模式篇章(3)——七种结构型模式

结构型设计模式主要思考的是如何将对象进行合理的布局来组成一个更大的功能体或者结构体&#xff0c;这个现在讲有点抽象&#xff0c;用大白话讲就是利用现有的对象进行组合或者配合&#xff0c;使得组合后的这个系统更加好。好是相对于不使用设计模式&#xff0c;按照自己的堆…

固定翼仿真的切换

delta固定翼飞行器模型 接着这篇文章文章链接&#xff0c;我们对飞行器模型进行改进&#xff0c; 我们知道&#xff0c;我们打开仿真模型 gazebo --verbose zephyr_ardupilot_demo.world 我们注意这最后一个语句 <model name"zephyr_delta_wing_demo">//加载z…

Cesium自定义电子围栏特效材质

1.new Cesium.Viewer中添加 requestWebgl1: true, 2. 编写材质&#xff0c;主要分三步 &#xff08;1&#xff09;定义MaterialProperty &#xff08;2&#xff09;设置材质 &#xff08;3&#xff09;添加材质 DynamicWallMaterial.js //定义材质对象及变量 function Dy…

IIS+SDK+VS2010+SP1+SQL server2012全套工具包及安装教程

前言 今天花了两个半小时安装这一整套配置&#xff0c;这个文章的目标是将安装时间缩短到1个小时 正文 安装步骤如下&#xff1a; VS2010 —> service pack 1 —>SQL server2012 —> IIS —> SDK 工具包链接如下&#xff1a; https://pan.baidu.com/s/1WQD-KfiUW…

uniapp使用wxml-to-canvas开发小程序保存canvas图片

微信小程序官方解决方案&#xff1a;wxml-to-canvas 使用wxml-to-canvas要知道一些前提条件 1、只能画view&#xff0c;text&#xff0c;image 2、每个元素必须要设置宽高 3、默认是flex布局&#xff0c;可以通过flexDirection: "column"来改变排列方式 4、文字 必…

[Kubernetes]8. K8s使用Helm部署mysql集群(主从数据库集群)

上一节讲解了K8s包管理工具Helm、使用Helm部署mongodb集群(主从数据库集群),这里来看看K8s使用Helm部署mysql集群(主从数据库集群) 一.Helm 搭建mysql集群 1.安装mysql不使用persistence(无本地存储) 无本地存储:当重启的时候,数据库消失 (1).打开官网的应用中心 打开应用中…

实现LCM在docker之间的通信

目录 1.docker容器互联 新建网络 连接容器 2.设置环境变量 3.在两个docker之间实现通信 1.docker容器互联 新建网络 $ docker network create -d bridge test-net 连接容器 运行一个容器并连接到新建的 test-net 网络: $ docker run -itd --name lcm_1 --network tes…

使用Docker-Compose部署MySQL一主二从同步高可用MHA集群

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容准备mysql一主二从同步集群一主二从同步集群规划需要安装docker和docker-compose命令形式安装安装docker安装docker-compose 宝塔面板形式安装 部署Master节点的docker-compose.yaml文件部署MySQL从节点1的docker-compose.…

约数个数和约数之和算法总结

知识概览 约数个数 基于算数基本定理&#xff0c;假设N分解质因数的结果为 可得对于N的任何一个约数d&#xff0c;有 因为N的每一个约数和~的一种选法是一一对应的&#xff0c;根据乘法原理可得&#xff0c; 一个数的约数个数为 约数之和 一个数的约数之和公式为 多项式乘积的…

尺寸链校核软件是什么?手机装配公差的含义是什么?让我们通过DTAS软件案例来解释。

尺寸公差软件 DTAS3D在智能手机装配过程中的应用非常重要&#xff0c;它能够帮助制造商提高产品质量和生产效率。这种软件可以帮助实现更高的装配精度&#xff0c;从而提升整体产品的质量。在这个案例中&#xff0c;DTAS3D的应用对于国产智能手机的装配过程起到了关键作用。 问…

【SpringBoot】Java MVC 集成 Swagger 生成 API 文档

使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面。官网: https://swagger.io/ Knife4j 是为Java MVC框架集成Swagger生成Api文档的增强解决方案。 <dependency><groupId>com.github.xiaoymin</groupI…

mysql基础-数据操作之增删改

目录 1.新增数据 1.1单条数据新增 1.2多条数据新增 1.3查询数据新增 2.更新 2.1单值更新 2.2多值更新 2.3批量更新 2.3.1 批量-单条件更新 2.3.2批量-多条件更新 2.4 插入或更新 2.5 联表更新 3.删除 本次分享一下数据库的DML操作语言。 操作表的数据结构&#xf…

VScode 画图插件

开源免费的插件 随着http://draw.io开源vs code插件之后&#xff0c;它一跃成为最强大的流程图工具。 目前http://draw.io支持3种文件后缀&#xff0c;你只需要新建3种后缀之一的文件就可以在vs code中画流程图&#xff0c;它们分别是&#xff1a; *.drawio*.dio*.drawio.sv…

Python调用Shell命令 (python, shell 混合编程)

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 Python经常被称作“胶水语言”&#xff0c;因为它能够轻易地操作其他程序&#xff0c;轻易地包装使用其他语言编写的库&#xff0c;也当然可以用Python调用Shell命令。 用Python调用Shell命令有如下几种方式&#xff1a; 1.…

ChatGPT付费创作系统V2.5.5独立版+前端

ChatGPT付费创作系统V2.5.5版本优化了很多细节&#xff0c;功能增加增加长篇写作功能。该版本为编译版无开源&#xff0c;本版本特别处理了后台弹窗、暗链网址。特别优化了数据库。升级过程中未发现任何BUG&#xff0c;全新安装或者升级安装均未出现400或者500错误&#xff0c;…