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

多线程篇-8--线程安全(死锁,常用保障安全的方法,安全容器,原子类,Fork/Join框架等)

1、线程安全和不安全定义

(1)、线程安全

线程安全是指一个类或方法在被多个线程访问的情况下可以正确得到结果,不会出现数据不一致或其他错误行为。

线程安全的条件

1、原子性(Atomicity)

  • 多个操作要么全部完成,要么一个也不完成,中间状态对外部不可见。

2、可见性(Visibility)

  • 一个线程对共享变量的修改对其他线程是立即可见的。

3、有序性(Ordering)

  • 操作的顺序应该按照预期的顺序执行,不会由于编译器优化或处理器乱序执行而改变。

4、互斥性(Mutual Exclusion)

  • 在任何时刻,只有一个线程可以访问共享资源,避免多个线程同时修改同一数据。

(2)、线程不安全

线程不安全是指一个类或方法在多线程环境下不能被多个线程安全地访问,可能会导致数据不一致或其他错误行为。

线程不安全可能会出现的问题:

1、数据竞争(Race Conditions)

多个线程同时访问和修改同一个共享变量,导致结果不可预测。
即:多个线程同时修改一个共享变量,可能导致结果达不到预期。

代码示例:

 public class Counter {private int counter = 0;public void incrementCounter() {counter++; // 不是原子操作,可能会导致数据竞争}}
2、内存可见性问题(Visibility Problems)

一个线程对共享变量的修改对其他线程不可见,导致数据不一致。

即:每个线程运行时都会先从主内存中读取变量到工作内存中保存副本。执行修改操作都是在自己的工作内存中进行的,修改结果会先保存到工作副本中,只有遇到合适的机制或处理完成后才会将修改的变量副本数据写回到主内存中。所以在此期间,即使修改了数据,可能结果也不会被其他线程知道,导致获取的还是之前的数据。

3、死锁(Deadlocks)

多个线程互相等待对方释放锁,导致程序挂起。

代码示例:

public class DeadlockExample {private final Object lock1 = new Object();private final Object lock2 = new Object();public void method1() {synchronized (lock1) {synchronized (lock2) {// 业务逻辑}}}public void method2() {synchronized (lock2) {synchronized (lock1) {// 业务逻辑}}}}

2、常用解决不安全方式

(1)、java.util.concurrent的工具类

java.util.concurrent中提供了很多保障线程安全的工具类,如BlockingQueue,CountdownLatch等,可以参考之前的博客了解下。

(2)、synchronized

使用 synchronized关键字实现同步,确保同一时间只有一个线程可以访问共享资源。

代码示例:

 public synchronized void incrementCounter() {counter++;}

(3)、Lock

使用Lock或和Condition结合的方式,可以实现更加灵活的锁机制,保证线程同步执行

(4)、Lock和synchronized区别

1、synchronized是一个关键字,可以直接应用于方法或代码块。Lock 是一个接口,提供了比synchronized 更丰富的锁操作。
2、synchronized当同步代码块或方法执行完毕或抛出异常时,锁会自动释放。Lock需要手动获取和释放锁,通常在 try-finally 块中使用,确保锁在任何情况下都能被释放。
3、synchronized锁是非公平的,即等待时间最长的线程不一定最先获得锁。ReentrantLock可以选择是否使用公平锁。公平锁确保等待时间最长的线程最先获得锁。

Lock lock = new ReentrantLock(true); // 公平锁

4、synchronized锁的粒度是对象级别的,即一个对象的多个同步方法之间会相互阻塞。Lock可以更细粒度地控制锁,允许多个锁实例,从而减少不必要的阻塞。
5、条件变量不一样,synchronized内使用Object类的wait和notify方法;Lock提供了Condition接口,通过await和signal方法实现线程等待唤醒机制。
代码示例

Lock lock = new ReentrantLock();Condition condition = lock.newCondition();try {lock.lock();// 等待条件condition.await();// 通知条件condition.signal();} catch (InterruptedException e) {// 处理中断异常} finally {lock.unlock();}

6、synchronized而言,获取锁的线程和等待获取锁的线程都是不可中断的;Lock可以通过灵活的机制控制是否可被中断。
Lock可中断获取锁代码示例:
如下的代码中,通过lock.lockInterruptibly()可中断的获取锁,那么被中断时会直接中断抛出异常;如果是lock.lock()获取锁,那么就和synchronized一样,任然会继续执行。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {private final Lock lock = new ReentrantLock();public void method() throws InterruptedException {try {System.out.println("Thread " + Thread.currentThread().getName() + " is trying to acquire the lock...");lock.lockInterruptibly(); // 可中断地获取锁,被中断时直接抛出中断异常System.out.println("Thread " + Thread.currentThread().getName() + " got the lock.");Thread.sleep(10000); // 模拟长时间操作} catch (InterruptedException e) {System.out.println("Thread " + Thread.currentThread().getName() + " was interrupted.");throw e;} finally {lock.unlock();}}public static void main(String[] args) {LockExample example = new LockExample();Thread t1 = new Thread(() -> {try {example.method();} catch (InterruptedException e) {System.out.println("Thread " + Thread.currentThread().getName() + " was interrupted.");}});Thread t2 = new Thread(() -> {try {example.method();} catch (InterruptedException e) {System.out.println("Thread " + Thread.currentThread().getName() + " was interrupted.");}});t1.start();t2.start();// 让主线程等待一段时间,确保t1已经进入同步代码块try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 中断t2线程t2.interrupt();}
}

(5)、ThreadLocal

使用 ThreadLocal变量,确保每个线程都有自己的独立副本,避免线程间的竞争。
线程安全问题的核心在于多个线程会对同一个临界区共享资源进行操作,如果每个线程都使用自己的“共享资源”,各自使用各自的,又互相不影响到彼此即让多个线程间达到隔离的状态,这样就不会出现线程安全的问题。ThreadLocal是一种“空间换时间”的方案,每个线程都会都拥有自己的“共享资源”无疑内存会大很多,但是由于不需要同步也就减少了线程可能存在的阻塞等待的情况从而提高的时间效率。

代码示例

public class ThreadSafeCounter {private ThreadLocal<Integer> counter = ThreadLocal.withInitial(() -> 0);public void incrementCounter() {counter.set(counter.get() + 1);}public int getCounter() {return counter.get();}
}

(6)、Redis分布式锁

以上都是基于单节点下的,如果是多节点集群模式,仍然不能保证整个系统的线程安全问题。
可以将服务的多个节点都配置到同一个redis连接,利用redis的setNx原子操作来实现锁的功能,如果set Key成功认为获取了锁,使用删除key实现解锁的功能,这个是实际应用中常用的。

redis分布式锁和synchronized的区别:
1、分布式锁是指在分布式环境下的多个节点之间控制并发访问的一种机制,而synchronized是在单个服务的线程之间进行同步控制;
2、分布式锁一般通过Redis等分布式数据库实现,可以在多个应用服务器之间共享;而synchronized则只能在单个应用进程内起作用。
3、分布式锁需要考虑分布式环境下的数据一致性问题,保证多个节点之间的数据同步;而synchronized只需要考虑单个进程内的数据同步问题。
4、Redis等分布式数据库提供的分布式锁机制可以实现比较灵活的锁定方式,如设置超时时间、可重入等功能;而synchronized没有这些灵活的操作。

(7)、使用安全容器

Java的集合容器主要有四大类别:List、Set、Queue、Map,常见的集合类ArrayList、LinkedList、HashMap这些容器都是非线程安全的容器。
如果有多个线程并发地访问这些容器时,就可能会出现问题。因此,在编写程序时,在多线程环境下必须要求程序员手动地在任何访问到这些容器的地方进行同步处理,这样导致在使用这些容器的时候非常地不方便。
所以java提供了线程安全的容器,其中按照底层实现原理可以分为同步容器和并发容器。这个在后面会介绍。

(8)、使用Atomic原子类

使用 java.util.concurrent.atomic 包中的原子类(如 AtomicInteger、AtomicLong 等),这些类提供了原子操作。

代码示例

import java.util.concurrent.atomic.AtomicInteger;
public class Counter {private AtomicInteger counter = new AtomicInteger(0);public void incrementCounter() {counter.incrementAndGet();}
}

3、安全容器

(1)、同步容器

1、概述

同步容器(Synchronized Containers)是 Java 提供的一种线程安全的集合类,它们通过在方法内部添加同步机制来确保线程安全。Java 标准库中的 Collections 类提供了一些静态方法,可以将普通的集合类转换为同步集合类。
同步容器可以简单地理解为使用synchronized实现同步后的容器。

2、常见的同步容器
(1)、Vector

Vector 是一个线程安全的动态数组,类似于 ArrayList,但它的方法都是同步的。

代码示例

 Vector<String> vector = new Vector<>();vector.add("Hello");vector.add("World");
(2)、Hashtable

Hashtable 是一个线程安全的哈希表,类似于 HashMap,但它的方法都是同步的。

代码示例

 Hashtable<String, String> hashtable = new Hashtable<>();hashtable.put("key1", "value1");hashtable.put("key2", "value2");
(3)、Collections.synchronizedList

将一个 List 转换为同步的 List

代码示例

 List<String> list = Collections.synchronizedList(new ArrayList<>());list.add("Hello");list.add("World");
(4)、Collections.synchronizedMap

将一个 Map 转换为同步的 Map

代码示例

 Map<String, String> map = Collections.synchronizedMap(new HashMap<>());map.put("key1", "value1");map.put("key2", "value2");
(5)、Collections.synchronizedSet

将一个 Set 转换为同步的 Set

代码示例

 Set<String> set = Collections.synchronizedSet(new HashSet<>());set.add("Hello");set.add("World");
3、同步容器的工作原理

同步容器通过在每个方法内部添加synchronized 关键字来实现线程安全。例如,Collections.synchronizedList 返回的列表对象的方法内部都会加上synchronized 关键字,确保同一时间只有一个线程可以访问该方法。

4、使用同步容器的注意事项

(1)、性能影响

  • 同步容器在高并发环境下可能会成为性能瓶颈,因为每次方法调用都会阻塞其他线程。
  • 对于高性能要求的场景,可以考虑使用 ConcurrentHashMapCopyOnWriteArrayList 等并发集合类。

(2)、外部同步

  • 尽管同步容器的方法是线程安全的,但在进行复合操作(如迭代,即遍历)时,仍然需要外部同步。

代码示例

 List<String> list = Collections.synchronizedList(new ArrayList<>());synchronized (list) {Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}

(2)、并发容器

1、概述

并发容器(Concurrent Containers)是专门为多线程环境设计的集合类,它们提供了比同步容器更高的并发性能和更好的扩展性。Java 提供了多种并发容器,这些容器在设计上考虑了多线程并发访问的场景,能够在高并发环境下保持良好的性能和安全性。

2、常见的并发容器
(1)、ConcurrentHashMap

ConcurrentHashMap是一个线程安全的哈希表,它允许多个线程同时读取和写入,而不会造成死锁。
内部使用分段锁(Segment)机制,允许多个线程同时访问不同的段,从而提高并发性能。

代码示例:

 import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();   // 正常当map用即可map.put("key1", "value1");map.put("key2", "value2");String value = map.get("key1");System.out.println(value); // 输出: value1}}
(2)、CopyOnWriteArrayList

CopyOnWriteArrayList是一个线程安全的列表,它在写操作时会复制整个数组,因此读操作不需要加锁,写操作也相对安全。
适用于读多写少的场景。

代码示例:

 import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListExample {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();   // 正常当List用即可list.add("Hello");list.add("World");for (String item : list) {System.out.println(item); // 输出: Hello, World}}}
(3)、ConcurrentLinkedQueue

ConcurrentLinkedQueue 是一个线程安全的无界非阻塞队列,适用于高并发环境。
内部使用链表结构,允许多个线程同时进行插入和删除操作。

代码示例:

     import java.util.concurrent.ConcurrentLinkedQueue;public class ConcurrentLinkedQueueExample {public static void main(String[] args) {ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();queue.offer("Hello");queue.offer("World");String item = queue.poll();System.out.println(item); // 输出: Hello}}
(4)、ConcurrentSkipListMap

ConcurrentSkipListMap 是一个线程安全的有序映射,类似于TreeMap,但它使用跳表(Skip List)实现,允许多个线程并发访问。
适用于需要有序存储且支持并发访问的场景。

代码示例:

  import java.util.concurrent.ConcurrentSkipListMap;public class ConcurrentSkipListMapExample {public static void main(String[] args) {ConcurrentSkipListMap<String, String> map = new ConcurrentSkipListMap<>();map.put("key1", "value1");map.put("key2", "value2");String value = map.get("key1");System.out.println(value); // 输出: value1}}
(5)、ConcurrentLinkedDeque

ConcurrentLinkedDeque 是一个线程安全的双端队列,适用于高并发环境。
内部使用链表结构,允许多个线程同时进行插入和删除操作。

代码示例:

 import java.util.concurrent.ConcurrentLinkedDeque;public class ConcurrentLinkedDequeExample {public static void main(String[] args) {ConcurrentLinkedDeque<String> deque = new ConcurrentLinkedDeque<>();deque.offerFirst("Hello");deque.offerLast("World");String item = deque.pollFirst();System.out.println(item); // 输出: Hello}}
(6)、CopyOnWriteArraySet

CopyOnWriteArraySet 是 Java 提供的一个线程安全的集合类,它是基于 CopyOnWriteArrayList 实现的。
CopyOnWriteArraySet 适用于读多写少的场景,因为它在的修改操作(如添加、删除)都会创建一个新的底层数组,并且在写操作期间锁定整个集合,确保操作的原子性和一致性。

代码示例:

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArraySet;public class CopyOnWriteArraySetExample {public static void main(String[] args) {// 创建一个 CopyOnWriteArraySetCopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();// 添加元素set.add("Apple");set.add("Banana");set.add("Cherry");// 检查元素是否存在System.out.println("Contains 'Banana': " + set.contains("Banana")); // 输出: Contains 'Banana': true// 删除元素set.remove("Banana");// 检查元素是否存在System.out.println("Contains 'Banana': " + set.contains("Banana")); // 输出: Contains 'Banana': false// 获取集合大小System.out.println("Size of set: " + set.size()); // 输出: Size of set: 2// 遍历集合System.out.println("Elements in set:");Iterator<String> iterator = set.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}// 输出: Apple, Cherry}
}
3、并发容器的特点

(1)、高并发性能
并发容器设计时考虑了多线程并发访问的场景,通常使用细粒度的锁或无锁算法,允许多个线程同时访问不同的部分,从而提高并发性能。

(2)、线程安全
并发容器在多线程环境下是安全的,不会导致数据不一致或其他错误行为。

(3)、扩展性
并发容器通常具有更好的扩展性,能够在高并发环境下保持良好的性能。

(4)、适用场景

  • ConcurrentHashMap:适用于需要线程安全的哈希表,读多写少的场景。
  • CopyOnWriteArrayList:适用于读多写少的场景,读操作不需要加锁。
  • ConcurrentLinkedQueue:适用于高并发环境下的队列操作。
  • ConcurrentSkipListMap:适用于需要有序存储且支持并发访问的场景。
  • ConcurrentLinkedDeque:适用于高并发环境下的双端队列操作。
4、同步容器和并发容器对比

同步容器:
同步容器通过在方法内部添加 synchronized 关键字来实现线程安全,使用起来非常简单。
例如,VectorHashtable 是直接提供的线程安全版本,无需额外的操作。

但同步容器在高并发环境下可能会成为性能瓶颈,因为每次方法调用都会阻塞其他线程。如,Vectoradd 方法在每次调用时都会加锁,导致其他线程无法同时进行操作。
尽管同步容器的方法是线程安全的,但在进行复合操作(如迭代)时,仍然需要外部同步。
同步容器的同步机制较为单一,无法灵活调整锁的粒度和类型。

并发容器:
并发容器设计时考虑了多线程并发访问的场景,通常使用细粒度的锁或无锁算法,允许多个线程同时访问不同的部分,从而提高并发性能。
如,ConcurrentHashMap 使用分段锁机制,允许多个线程同时访问不同的段。
并发容器在多线程环境下是安全的,不会导致数据不一致或其他错误行为。
并发容器提供了更多的灵活性,允许开发者根据具体的并发需求选择合适的锁机制和数据结构。如,CopyOnWriteArrayList 适用于读多写少的场景,ConcurrentLinkedQueue 适用于高并发环境下的队列操作。
并发容器通常提供了更多的高级功能,如 ConcurrentHashMap 的 computeIfAbsent 方法,可以在并发环境下安全地进行计算。

但并发容器的使用和理解相对复杂,需要开发者对并发编程有较深入的理解。如,ConcurrentHashMap 的分段锁机制需要理解其内部实现才能有效使用。
并发容器在初始化时可能会有一定的开销,但这种开销通常在后续的高并发操作中会被抵消。

对比:
在这里插入图片描述

5、总结

对于简单的同步需求和低并发场景,同步容器是一个不错的选择;而对于复杂的同步需求和高并发场景,建议使用并发容器。

4、Fork/Join框架

(1)、概述

Fork/Join 框架是 Java 中用于实现并行任务处理的一种高级并发框架。它特别适用于可以分解成多个子任务并最终合并结果的场景。
Fork/Join 框架的核心思想是“分而治之”,通过递归地将大任务分解成小任务,然后将这些小任务并行处理,最后合并各个子任务的结果。

(2)、主要组件

1、ForkJoinPool

  • ForkJoinPoolFork/Join 框架的执行器,负责管理和调度任务。
  • 它使用工作窃取(Work Stealing)算法来提高任务的并行处理效率。工作窃取算法允许空闲的工作线程从其他忙碌的工作线程的任务队列中“窃取”任务来执行,从而最大化 CPU 的利用率。

2、RecursiveTask

  • RecursiveTask 是一个抽象类,用于表示可以返回结果的任务。
  • 继承 RecursiveTask 类并实现 compute 方法,该方法定义了任务的执行逻辑,包括任务的分解和结果的合并。

3、RecursiveAction

  • RecursiveAction 是一个抽象类,用于表示不返回结果的任务。
  • 继承 RecursiveAction 类并实现 compute 方法,该方法定义了任务的执行逻辑,包括任务的分解和执行。
(3)、工作流程

1、任务提交

  • 将任务提交给 ForkJoinPool,通常通过调用 invoke 方法来启动任务(会调用任务的compute方法)。

2、任务分解

  • 在任务的 compute 方法中,将大任务分解成多个子任务,使用 fork 方法将子任务提交给 ForkJoinPool

3、任务执行

  • ForkJoinPool 负责调度和执行这些子任务,使用工作窃取算法来优化任务的并行处理。即:要包含最终子任务的处理逻辑。

4、结果合并

  • 子任务完成后,使用 join 方法获取子任务的结果,并在 compute 方法中合并这些结果。
(4)、示例代码

假设我们需要计算一个大数组的总和,可以使用 Fork/Join 框架来实现并行计算。

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;public class ForkJoinSumCalculator extends RecursiveTask<Long> {private final long[] array;private final int start;private final int end;private static final int THRESHOLD = 1000; // 阈值,用于决定是否分解任务public ForkJoinSumCalculator(long[] array, int start, int end) {this.array = array;this.start = start;this.end = end;}@Overrideprotected Long compute() {if (end - start <= THRESHOLD) {  // 当任务足够小时,直接计算结果long sum = 0;for (int i = start; i < end; i++) {sum += array[i];}return sum;} else {    // 当任务比较大时,做任务拆分int middle = (start + end) / 2;ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(array, start, middle);   // 构建的子任务,对主任务分解ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(array, middle, end);// 提交子任务leftTask.fork();    // 提交子任务,如果子任务任然超出阈值,还会走else部分进行分解任务(相当于递归),直到任务小于阈值会走上面的if部分处理得到结果。rightTask.fork();// 合并子任务的结果return leftTask.join() + rightTask.join();   // 结果直接通过join返回}}public static void main(String[] args) {long[] array = new long[1000000];for (int i = 0; i < array.length; i++) {array[i] = i;}ForkJoinPool forkJoinPool = new ForkJoinPool();    // 创建调度器ForkJoinSumCalculator task = new ForkJoinSumCalculator(array, 0, array.length);  // 创建任务,继承RecursiveTask(需要返回)或RecursiveAction(不需要返回)long result = forkJoinPool.invoke(task);   // 调度执行任务(invoke实际只是调用compute方法),获取任务最终结果System.out.println("Sum: " + result);}
}
(5)、总结

使用 Fork/Join 框架,可以显著提高多核处理器的利用率,从而提升程序的性能。适用于可以分解成多个子任务并最终合并结果的场景。开发者只需关注任务的分解和合并逻辑。

但是,任务分解和合并需要一定的开销,特别是对于小任务,可能会导致性能下降。需要合理设置阈值,平衡任务分解的开销和并行处理的收益。大量的任务分解会导致内存开销增加,特别是在任务数量较多时。

5、原子操作类

Atomic类是JUC提供的一组原子操作的封装类,它们位于java.util.concurrent.atomic中。Atomic包一共提供了13个类。
Atomic类是通过无锁(lock-free)的方式实现的线程安全(thread-safe)访问。它的主要原理是利用了CAS(Compare and Set)。

Atomic包中的类基本的特性就是在多线程环境下,当有多个线程同时对单个(包括基本类型及引用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量的值进行更新时,仅有一个线程能成功,而未成功的线程可以向自旋锁一样,继续尝试,一直等到执行成功。

(1)、原子基本数据类型

如:AtomicInteger是一个线程安全的整数类,提供了原子性的增减操作和其他常用的原子操作。

1、主要方法

  • int get():获取当前值。
  • void set(int newValue):设置新值。
  • int getAndSet(int newValue):获取当前值并设置新值。 // 都是先用后增思路(即:a++)
  • int getAndIncrement():获取当前值并自增1。
  • int getAndDecrement():获取当前值并自减1。
  • int getAndAdd(int delta):获取当前值并增加指定值。
  • boolean compareAndSet(int expect, int update):如果当前值等于预期值,则设置新值并返回 true,否则返回 false

示例代码

import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerExample {public static void main(String[] args) {AtomicInteger atomicInt = new AtomicInteger(0);// 自增1int value = atomicInt.getAndIncrement();System.out.println("Value after increment: " + value); // 输出: Value after increment: 0// 设置新值atomicInt.set(10);System.out.println("New value: " + atomicInt.get()); // 输出: New value: 10// 比较并设置boolean result = atomicInt.compareAndSet(10, 20);System.out.println("Compare and set result: " + result); // 输出: Compare and set result: trueSystem.out.println("Current value: " + atomicInt.get()); // 输出: Current value: 20}
}
(2)、原子数组

如:AtomicIntegerArray` 是一个线程安全的整数数组类,提供了对数组元素的原子操作。

1、主要方法

  • int get(int index):获取指定索引处的值。
  • void set(int index, int value):设置指定索引处的值。
  • int getAndSet(int index, int value):获取指定索引处的值并设置新值。
  • int getAndIncrement(int index):获取指定索引处的值并自增1。
  • int getAndDecrement(int index):获取指定索引处的值并自减1。
  • int getAndAdd(int index, int delta):获取指定索引处的值并增加指定值。
  • boolean compareAndSet(int index, int expect, int update):如果指定索引处的值等于预期值,则设置新值并返回 true,否则返回 false

示例代码

import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayExample {public static void main(String[] args) {int[] values = {1, 2, 3};AtomicIntegerArray atomicIntArray = new AtomicIntegerArray(values);// 获取指定索引处的值int value = atomicIntArray.get(0);System.out.println("Value at index 0: " + value); // 输出: 1// 返回索引处的值,并自增1(返回结果为自增前的结果,即先用后加,类似a++)value = atomicIntArray.getAndIncrement(0);System.out.println("Value after increment at index 0: " + value); // 输出:  1// 比较并设置值(上面自增过了,所以0处索引的值为2和预期值相等,返回true,同时在设置为10)boolean result = atomicIntArray.compareAndSet(0, 2, 10);System.out.println("Compare and set result: " + result); // 输出: trueresult = atomicIntArray.compareAndSet(0, 1, 20);  System.out.println("Compare and set result: " + result);    // 输出: falseSystem.out.println("Current value at index 0: " + atomicIntArray.get(0)); // 输出: 10}
}
(3)、原子更新引用类

如:AtomicReference是一个线程安全的引用类,提供了对对象引用的原子操作。

1、主要方法

  • T get():获取当前值。
  • void set(T value):设置新值。
  • T getAndSet(T value):获取当前值并设置新值。
  • boolean compareAndSet(T expect, T update):如果当前值等于预期值,则设置新值并返回 true,否则返回 false

示例代码

import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceExample {public static void main(String[] args) {AtomicReference<String> atomicRef = new AtomicReference<>("Hello");// 获取当前值String value = atomicRef.get();System.out.println("Initial value: " + value); // 输出: Initial value: Hello// 设置新值atomicRef.set("World");System.out.println("New value: " + atomicRef.get()); // 输出: New value: World// 比较并设置boolean result = atomicRef.compareAndSet("World", "Java");System.out.println("Compare and set result: " + result); // 输出: Compare and set result: trueSystem.out.println("Current value: " + atomicRef.get()); // 输出: Current value: Java}
}
(4)、原子更新字段类

如:AtomicIntegerFieldUpdater是一个用于更新对象字段的原子类,适用于需要对对象的某个字段进行原子操作的场景。它通过反射机制来实现对字段的原子操作。

1、主要方法

  • static AtomicIntegerFieldUpdater<T> newUpdater(Class<T> tclass, String fieldName):创建一个新的 AtomicIntegerFieldUpdater 实例。
  • int get(T obj):获取指定对象的字段值。
  • void set(T obj, int newValue):设置指定对象的字段值。
  • int getAndSet(T obj, int newValue):获取指定对象的字段值并设置新值。
  • int getAndIncrement(T obj):获取指定对象的字段值并自增1。
  • int getAndDecrement(T obj):获取指定对象的字段值并自减1。
  • int getAndAdd(T obj, int delta):获取指定对象的字段值并增加指定值。
  • boolean compareAndSet(T obj, int expect, int update):如果指定对象的字段值等于预期值,则设置新值并返回 true,否则返回 false

示例代码

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;class MyObject {volatile int value;
}public class AtomicIntegerFieldUpdaterExample {
public static void main(String[] args) {// 定义MyObject类的更新对象,指定value属性AtomicIntegerFieldUpdater<MyObject> updater = AtomicIntegerFieldUpdater.newUpdater(MyObject.class, "value");//  MyObject 实例对象ojMyObject obj = new MyObject();// 通过更新类,赋值obj的value属性为0updater.set(obj, 0);System.out.println("Initial value: " + updater.get(obj)); // 输出: Initial value: 0// 通过更新类,将obj的value属性自增1int value = updater.getAndIncrement(obj);System.out.println("Value after increment: " + value); // 输出: Value after increment: 0// 通过更新类,将obj的value属性对比和重新赋值boolean result = updater.compareAndSet(obj, 1, 10);System.out.println("Compare and set result: " + result); // 输出: Compare and set result: true// 通过更新类,获取obj的value属性值System.out.println("Current value: " + updater.get(obj)); // 输出: Current value: 10}
}

学海无涯苦作舟!!!

相关文章:

多线程篇-8--线程安全(死锁,常用保障安全的方法,安全容器,原子类,Fork/Join框架等)

1、线程安全和不安全定义 &#xff08;1&#xff09;、线程安全 线程安全是指一个类或方法在被多个线程访问的情况下可以正确得到结果&#xff0c;不会出现数据不一致或其他错误行为。 线程安全的条件 1、原子性&#xff08;Atomicity&#xff09; 多个操作要么全部完成&a…...

el-select的搜索功能

el-select的相关信息&#xff1a; 最基本信息 v-model的值为当前被选中的el-option的 value 属性值 :label是选择器可以看到的内容 过滤搜索 普通过滤搜索 <el-selectv-model"selectedCountry"placeholder"请选择国家"filterable:loading"lo…...

MFC实现全屏功能

之前全屏都是参考&#xff1a; MFC单文档&#xff08;SDI&#xff09;全屏程序的实现 主要思路就是将各种菜单工具栏隐藏恢复。 随着MFC的升级&#xff0c;MFC框架本身就具备了全屏的功能。 微软有一个全屏实现类&#xff1a; CFullScreenImpl Class managing full-screen mod…...

网络安全技术详解:虚拟专用网络(VPN) 安全信息与事件管理(SIEM)

虚拟专用网络&#xff08;VPN&#xff09;详细介绍 虚拟专用网络&#xff08;VPN&#xff09;通过在公共网络上创建加密连接来保护数据传输的安全性和隐私性。 工作原理 VPN的工作原理涉及建立安全隧道和数据加密&#xff1a; 隧道协议&#xff1a;使用协议如PPTP、L2TP/IP…...

v-model 根据后端接口返回的数据动态地确定要绑定的变量

在 Vue 中&#xff0c;v-model 是用于创建双向绑定的指令。通常&#xff0c;它用于与组件或表单元素的值进行绑定。但有时你可能需要根据后端接口返回的数据动态地确定要绑定的变量。 你可以通过以下步骤来实现这个需求&#xff1a; 步骤 1: 获取后端接口数据 首先&#xff…...

图形开发基础之在WinForms中使用OpenTK.GLControl进行图形绘制

前言 GLControl 是 OpenTK 库中一个重要的控件&#xff0c;专门用于在 Windows Forms 应用程序中集成 OpenGL 图形渲染。通过 GLControl&#xff0c;可以轻松地将 OpenGL 的高性能图形绘制功能嵌入到传统的桌面应用程序中。 1. GLControl 的核心功能 OpenGL 渲染上下文&…...

离散数学重点复习

第一章.集合论 概念 1.集合是不能精确定义的基本数学概念.通常是由指定范围内的满足给定条件的所有对象聚集在一起构成的 2.制定范围内的每一个对象称为这个集合的元素 3.固定符号如下: N:自然数集合 Z:整数集合 Q:有理数集合 R:实数集合 C:复数集合 4.集合中的元素是…...

Javaweb梳理21——Servlet

Javaweb梳理21——Servlet 21 Servlet21.1 简介21.3 执行流程21.4 生命周期4.5 方法介绍21.6 体系结构21.7 urlPattern配置21.8 XML配置 21 Servlet 21.1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。使用Servlet就可以实现&…...

推荐学习笔记:矩阵补充和矩阵分解

参考&#xff1a; 召回 fun-rec/docs/ch02/ch2.1/ch2.1.1/mf.md at master datawhalechina/fun-rec GitHub 业务 隐语义模型与矩阵分解 协同过滤算法的特点&#xff1a; 协同过滤算法的特点就是完全没有利用到物品本身或者是用户自身的属性&#xff0c; 仅仅利用了用户与…...

etcd分布式存储系统快速入门指南

在分布式系统的复杂世界中&#xff0c;确保有效的数据管理至关重要。分布式可靠的键值存储在维护跨分布式环境的数据一致性和可伸缩性方面起着关键作用。 在这个全面的教程中&#xff0c;我们将深入研究etcd&#xff0c;这是一个开源的分布式键值存储。我们将探索其基本概念、特…...

解决VUE3 Vite打包后动态图片资源不显示问题

解决VUE3 Vite打包后动态图片资源不显示问题 <script setup> let url ref()const setimg (item)>{let src ../assets/image/${e}.pngurl.value src }</script><template><div v-for"item in 6"><h1 click"setimg(item)"…...

大数据新视界 -- 大数据大厂之 Hive 临时表与视图:灵活数据处理的技巧(上)(29 / 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

Android学习14--charger

1 概述 最近正好在做关机充电这个&#xff0c;就详细看看吧。还是本着保密的原则&#xff0c;项目里的代码也不能直接用&#xff0c;这里就用的Github的。https://github.com/aosp-mirror 具体位置是&#xff1a;https://github.com/aosp-mirror/platform_system_core/tree/mai…...

页面开发样式和布局入门:Vite + Vue 3 + Less

页面开发样式和布局入门&#xff1a;Vite Vue 3 Less 引言 在现代前端开发中&#xff0c;样式和布局是页面开发的核心部分。随着技术的不断发展&#xff0c;Vite、Vue 3和Less等工具和框架的出现&#xff0c;使得前端开发变得更加高效和灵活。然而&#xff0c;尽管这些工具…...

瑞芯微RK3566/RK3568开发板安卓11固件ROOT教程,Purple Pi OH演示

本文介绍RK3566/RK3568开发板Android11系统&#xff0c;编译ROOT权限固件的方法。触觉智能Purple Pi OH鸿蒙开发板演示&#xff0c;搭载了瑞芯微RK3566四核处理器&#xff0c;Laval鸿蒙社区推荐开发板&#xff0c;已适配全新OpenHarmony5.0 Release系统&#xff0c;SDK源码全开…...

Netty 入门应用:结合 Redis 实现服务器通信

在上篇博客中&#xff0c;我们了解了 Netty 的基本概念和架构。本篇文章将带你深入实践&#xff0c;构建一个简单的 Netty 服务端&#xff0c;并结合 Redis 实现一个数据存取的示例。在这个场景中&#xff0c;Redis 作为缓存存储&#xff0c;Netty 作为服务端处理客户端请求。通…...

试题转excel;pdf转excel;试卷转Excel,word试题转excel

一、问题描述 一名教师朋友&#xff0c;偶尔会需要整理一些高质量的题目到excel中 以往都是手动复制搬运&#xff0c;几百道题几乎需要一个下午的时间 关键这些事&#xff0c;枯燥无聊费眼睛&#xff0c;实在是看起来就很蠢的工作 就想着做一个工具&#xff0c;可以自动处理…...

查看网卡设备Bus号

在Linux系统中&#xff0c;通过ip命令能够看到网卡设备的名称&#xff0c;那么怎么看这个网卡设备对应的硬件设备以及Bus号&#xff1f; 例如在下面的虚拟机中能够看到有一个网口名为enp1s0 如何查看这个设备对应的Bus编号&#xff0c;可以在/sys中找到对应的设备 ll /sys/cl…...

鸿蒙Next星河版高级用例之网络请求和自适应布局以及响应式布局

目录&#xff1a; 1、发起网络请求的两种方式第一种使用httpRequest发送http的请求&#xff1a;1.1、在进行网络请求前&#xff0c;您需要在module.json5文件中申明网络访问权限1.2、GET 请求1.3、POST请求1.4、处理响应的结果第二种使用axios发送http的请求&#xff1a;1.1、在…...

鸿蒙技术分享:敲鸿蒙木鱼,积____功德——鸿蒙元服务开发:从入门到放弃(3)...

本文是系列文章&#xff0c;其他文章见&#xff1a;敲鸿蒙木鱼&#xff0c;积____功德&#x1f436;&#x1f436;&#x1f436;——鸿蒙元服务开发&#xff1a;从入门到放弃(1)敲鸿蒙木鱼&#xff0c;积____功德&#x1f436;&#x1f436;&#x1f436;——鸿蒙元服务开发&am…...

Hadoop生态圈框架部署 伪集群版(六)- MySQL安装配置

文章目录 前言一、MySQL安装与配置1. 安装MySQL2. 安装MySQL服务器3. 启动MySQL服务并设置开机自启动4. 修改MySQL初始密码登录5. 设置允许MySQL远程登录6. 登录MySQL 卸载1. 停止MySQL服务2. 卸载MySQL软件包3. 删除MySQL配置文件及数据目录 前言 在本文中&#xff0c;我们将…...

【Docker】创建Docker并部署Web站点

要在服务器上创建Docker容器&#xff0c;并在其中部署站点&#xff0c;你可以按照以下步骤操作。我们将以Flask应用为例来说明如何完成这一过程。 1. 准备工作 确保你的服务器已经安装了Docker。如果没有&#xff0c;请根据官方文档安装&#xff1a; Docker 安装指南 2. 创…...

实验七 用 MATLAB 设计 FIR 数字滤波器

实验目的 加深对窗函数法设计 FIR 数字滤波器的基本原理的理解。 学习用 Matlab 语言的窗函数法编写设计 FIR 数字滤波器的程序。 了解 Matlab 语言有关窗函数法设计 FIR 数字滤波器的常用函数用法。 掌握 FIR 滤波器的快速卷积实现原理。 不同滤波器的设计方法具有不同的优…...

学习ESP32开发板安装鸿蒙操作系统(新板子esp32c3不支持)

鸿蒙LiteOS网址&#xff1a;LiteOS: Huawei LiteOS开源代码官方主仓库.LiteOS Studio 开发工具请访问https://gitee.com/LiteOS/LiteOS_Studio 失败的实践记录见&#xff1a;完全按照手册win10里装Ubuntu 虚拟机然后编译ESP32&#xff08;主要是想针对ESP32C3和S3&#xff09;…...

asp.net core过滤器应用

筛选器类型 授权筛选器 授权过滤器是过滤器管道的第一个被执行的过滤器&#xff0c;用于系统授权。一般不会编写自定义的授权过滤器&#xff0c;而是配置授权策略或编写自定义授权策略。简单举个例子。 using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCo…...

力扣面试题 31 - 特定深度节点链表 C语言解法

题目&#xff1a; 给定一棵二叉树&#xff0c;设计一个算法&#xff0c;创建含有某一深度上所有节点的链表&#xff08;比如&#xff0c;若一棵树的深度为 D&#xff0c;则会创建出 D 个链表&#xff09;。返回一个包含所有深度的链表的数组。 示例&#xff1a; 输入&#xf…...

WordPress阅读文章显示太慢的处理

有两种方式&#xff0c; 1. 完全静态化。 动态都变成html&#xff0c;不再查数据库就快了。 但尝试了几个插件&#xff0c;都未成功。算了后面再研究。 2. cache缓存 用了WP Super Cache测试了一下&#xff0c;打开过一次后&#xff0c;文章秒开&#xff0c;也算达到了要求…...

关于多个线程共享一个实例对象

在多线程环境中&#xff0c;多个线程可能同时调用同一个对象的实例方法&#xff0c;这时候需要考虑如何保证线程安全。理解不同场景下的线程安全性是至关重要的&#xff0c;特别是当方法涉及共享状态时。 1. 共享实例与方法执行 共享实例&#xff1a;多个线程共享同一个实例对…...

【C++】printf 函数详解与格式化输出控制

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;printf 基础用法1.1 printf 的常见占位符1.2 占位符与参数的对应关系1.3 换行控制示例&#xff1a; &#x1f4af;格式化输出控制2.1 输出宽度控制2.1.1 指定最小宽度 2.2 …...

HDFS 操作命令

在现代的企业环境中&#xff0c;单机容量往往无法存储大量数据&#xff0c;需要跨机器存储。统一管理分布在 集群上的文件系统称为 分布式文件系统 。 HDFS &#xff08; Hadoop Distributed File System &#xff09;是 Apache Hadoop 项目的一个子项目&#xff0c; Hadoo…...