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

在这里插入图片描述

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

  • ✅典型解析
  • 🟢拓展知识仓
    • ☑️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() 等操作

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

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

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

相关文章

城堡世界源码

随着数字技术的飞速发展和人们对于娱乐需求的不断提升&#xff0c;城堡世界源码开发逐渐成为了新的热门话题。城堡世界是一个集潮流、艺术、科技于一体的数字娱乐新领域&#xff0c;通过将虚拟现实、增强现实等技术融入传统玩具设计中&#xff0c;为玩家们带来了全新的互动体验…

建站为什么需要服务器?(Web服务器与计算机对比)

​  在部署网站时&#xff0c;底层基础设施在确保最佳性能、可靠性和可扩展性方面发挥着至关重要的作用。虽然大多数人都熟悉个人计算机 (PC) 作为日常工作和个人任务的设备&#xff0c;但 PC 和 Web 服务器之间存在显著差异。在这篇文章中&#xff0c;我们将讨论这些差异是什…

拼多多API的未来:无限可能性和创新空间

拼多多&#xff0c;作为中国电商市场的巨头之一&#xff0c;自成立以来一直保持着高速的发展态势。其API的开放为开发者提供了无限的可能性和创新空间&#xff0c;使得更多的商业逻辑和功能得以实现。本文将深入探讨拼多多API的未来发展&#xff0c;以及它所具备的无限可能性和…

Python基础学习(一)

Python基础语法学习记录 输出 将结果或内容呈现给用户 print("休对故人思故国&#xff0c;且将新火试新茶&#xff0c;诗酒趁年华") # 输出不换行&#xff0c;并且可以指定以什么字符结尾 print("青山依旧在",end ",") print("几度夕阳红…

2024-01-03 无重叠区间

435. 无重叠区间 思路&#xff1a;和最少数量引爆气球的箭的思路基本都是一致了&#xff01;贪心就是比较左边的值是否大于下一个右边的值 class Solution:def eraseOverlapIntervals(self, points: List[List[int]]) -> int:points.sort(keylambda x: (x[0], x[1]))# 比较…

入驻抖店的费用是多少?最新具体费用详情!

我是电商珠珠 抖店的入驻费用是新手比较关心的问题&#xff0c;网上的说法不一&#xff0c;有说开店要几w的&#xff0c;还有的说不要钱的&#xff0c;什么说法都有。 搞得想要开店的人&#xff0c;心有点慌&#xff0c;害怕超出自己的预算。 接下来我就跟大家详细讲一下&am…

Java中异常处理-详解

异常&#xff08;Exception&#xff09; JVM 默认处理方案 把异常的名称&#xff0c;异常的原因&#xff0c;及异常出错的位置等信息输出在控制台程序停止执行 异常类型 编译时异常必须显示处理&#xff0c;否则程序会发生错误&#xff0c;无法通过编译运行时异常无需显示处理…

数据泄密零容忍:揭秘迅软科技文件加密系统的保密奥秘!

企事业单位内部的数据机密性至关重要&#xff0c;但机密数据往往以电子文档形式存储&#xff0c;并借助多样化的传播手段&#xff0c;导致文件泄密事件频发。无论是员工误操作导致的终端泄密&#xff0c;还是黑客入侵窃取机密数据&#xff0c;都可能导致重要文件被非法获取&…

使用echarts制作柱状图、折线图,并且下方带表格

实现效果: 调试地址: https://echarts.apache.org/examples/zh/editor.html?cline-simple 源码: option { title: { left: center, top: 0, text: 2022-05月 制造产量 达成情况(单位: 吨) (图1)\n\n集团目标产量: 106,675吨 集团实际产量: 2,636吨, text…

Springboot+vue的工作流程管理系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的工作流程管理系统(有报告)&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的工作流程管理系统&#xff0c;采用M&#xff08;model&#xff09;V&am…

SENet实现遥感影像场景分类

今天我们分享SENet实现遥感影像场景分类。 数据集 本次实验我们使用的是NWPU-RESISC45 Dataset。NWPU Dataset 是一个遥感影像数据集&#xff0c;其中 NWPU-RESISC45 Dataset 是由西北工业大学创建的遥感图像场景分类可用基准&#xff0c;该数据集包含像素大小为 256*256 共计 …

1. seaborn-可视化统计关系

统计分析是了解数据集中的变量如何相互关联以及这些关系如何依赖于其他变量的过程。可视化是此过程的核心组件&#xff0c;这是因为当数据被恰当地可视化时&#xff0c;人的视觉系统可以看到指示关系的趋势和模式。 这里介绍三个seaborn函数。我们最常用的是relplot()。这是一…

golang实现加密解密文档

golang实现加密解密文档 package mainimport ("bytes""crypto/aes""crypto/cipher""crypto/rand""encoding/base64""flag""fmt""io""io/ioutil" )func main() {encodePtr : flag.…

阿赵UE学习笔记——8、贴图导入设置

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的用法&#xff0c;这次来说一下贴图的导入设置。   在内容浏览器里面可以看到纹理类型的资源&#xff0c;就是贴图了&#xff0c;鼠标悬浮在上面可以看到这个纹理贴图的信息&#xff1a; 双击纹理贴图…

Vue3技术解析(小册子)

随着 Vue 3 正式版本的发布&#xff0c;未来 Vue 3 将会成为前端的主流框架&#xff0c;这个毋庸置疑。Vue 3 在使用方面会兼容部分 Vue 2.x 的特性&#xff0c;比如 options API。 所以&#xff0c;究竟是要先学习 Vue 2 打好基础&#xff0c;还是直接学习 Vue 3 呢&#xff…

基于书生·浦语大模型应用开发范式介绍

文章目录 大模型应用开发范式LangChain简介构建向量数据库搭建知识库助手RAG方案优化建议 大模型应用开发范式 通用大模型的优势&#xff1a; 强大的语言理解、指令跟随、语言生成的能力可以理解用户自然语言的指令具有强大的知识储备和一定的逻辑推理能力。 通用大模型局限…

MongoDB快速实战与基本原理

MongoDB 介绍 什么是 MongoDB MongoDB 是一个文档数据库&#xff08;以 JSON 为数据模型&#xff09;&#xff0c;由 C 语言编写&#xff0c;旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。文档来自于“JSON Document”&#xff0c;并非我们一般理解的 PDF、WORD 文档…

构建安全可靠的系统:第二十一章到附录 A

第二十一章&#xff1a;建立安全和可靠性文化 原文&#xff1a;21. Building a Culture of Security and Reliability 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 作者&#xff1a;Heather Adkins 与 Peter Valchev&#xff0c;Felix Grbert&#xff0c;Ana Oprea…

计算机体系结构----重排序缓冲(ROB)

ROB的思想&#xff1a;不按顺序完成指令&#xff0c;但在使结果对体系结构状态可见之前重新排序 当指令被解码时&#xff0c;它会在 ROB 中保留下一个顺序条目当指令完成时&#xff0c;它将结果写入 ROB 条目当指令在 ROB 中最早并且无一例外地完成时&#xff0c;其结果移动到…

Java-布隆过滤器的实现

文章目录 前言一、概述二、误差率三、hash 函数的选择四、手写布隆过滤器五、guava 中的布隆过滤器 前言 如果想要判断一个元素是不是在一个集合里&#xff0c;一般想到的是将所有元素保存起来&#xff0c;然后通过比较确定。链表&#xff0c;树等等数据结构都是这种思路&…