多线程篇-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}
}

学海无涯苦作舟!!!

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

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

相关文章

自动驾驶决策规划算法-路径决策算法:二次规划

本文为学习自动驾驶决策规划算法第二章第四节(中) 路径二次规划算法》的学习笔记。 1 二次型 二次型的形式为 1 2 x T H x f T x \begin{equation} \frac{1}{2}\boldsymbol{x}^TH\boldsymbol{x}f^T\boldsymbol{x} \end{equation} 21​xTHxfTx​​ 约束 A e q x b e q \be…

AI开发-数据可视化库-Seaborn

1 需求 概述 Seaborn 是一个基于 Python 的数据可视化库&#xff0c;它建立在 Matplotlib 之上。其主要目的是使数据可视化更加美观、方便和高效。它提供了高层次的接口和各种美观的默认主题&#xff0c;能够帮助用户快速创建出具有吸引力的统计图表&#xff0c;用于数据分析和…

相交链表和环形链表

&#xff08;一&#xff09;相交链表 相交链表 思路&#xff1a;先分别计算出A列表和B列表的长度&#xff0c;判断它们的尾节点是否相等&#xff0c;如果不相等就不相交&#xff0c;直接返回空。然后让两个列表中的长的列表先走它们的差距步&#xff0c;然后再一起走&#xff…

[Redis#12] 常用类型接口学习 | string | list

目录 0.准备 1.string get | set set_with_timeout_test.cpp set_nx_xx_test.cpp mset_test.cpp mget_test.cpp getrange_setrange_test.cpp incr_decr_test.cpp 2.list lpush_lrange_test.cpp rpush_test.cpp lpop_rpop_test.cpp blpop_test.cpp llen_test.cpp…

A054-基于Spring Boot的青年公寓服务平台的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

【经典】星空主题的注册界面HTML,CSS,JS

目录 界面展示 完整代码 说明&#xff1a; 这是一个简单的星空主题的注册界面&#xff0c;使用了 HTML 和 CSS 来实现一个背景为星空效果的注册页面。 界面展示 完整代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8&…

TiDB 优化器丨执行计划和 SQL 算子解读最佳实践

作者&#xff1a; TiDB社区小助手 原文来源&#xff1a; https://tidb.net/blog/5edb7933 导读 在数据库系统中&#xff0c;查询优化器是数据库管理系统的核心组成部分&#xff0c;负责将用户的 SQL 查询转化为高效的执行计划&#xff0c;因而会直接影响用户体感的性能与稳…

位运算在嵌入式系统开发中的应用

目录 一、数据存储与节省 “绝技” 1.1. 传感器数据存储挑战 1.2. 位运算解决方案 1.2.1. 数据整合 1.2.2. 数据提取 1.3. 收益分析 二、硬件控制 “精准操纵术” 2.1. 位运算操控硬件寄存器的实例 2.2. 位运算在硬件控制中的优势 2.3. 电机驱动芯片寄存器控制示例 …

设置redis

1.https://github.com/tporadowski/redis/releases下载对应版本 解压 启动redis临时服务 在 redis 文件夹下 cmd 输入redis-server.exe redis.windows.conf 临时服务启动 从新打开一个cmd 运行redis-cli 输入ping 启动成功 命令行输入shutdown关闭服务 创建永久服务 在…

排序学习整理(1)

1.排序的概念及运用 1.1概念 排序&#xff1a;所谓排序&#xff0c;就是使⼀串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作&#xff0c;以便更容易查找、组织或分析数据。 1.2运用 购物筛选排序 院校排名 1.3常见排序算法 2.实…

【Rust】unsafe rust入门

这篇文章简单介绍下unsafe rust的几个要点 1. 解引用裸指针 裸指针其实就是C或者说C的指针&#xff0c;与C的指针不同的是&#xff0c;Rust的裸指针还是要分为可变和不可变&#xff0c;*const T 和 *mut T&#xff1a; 基于引用创建裸指针 let mut num 5;let r1 &num …

# 01_Python基础到实战一飞冲天(三)--python面向对象(一)--简单类

01_Python基础到实战一飞冲天&#xff08;三&#xff09;–python面向对象&#xff08;一&#xff09;–简单类 一、面向对象-01-基本概念 1、面向对象(OOP) 面向对象编程 —— Object Oriented Programming 简写 OOP。 2、面向对象(OOP) 学习目标 了解 面向对象 基本概念…

Java 基础知识与核心概念

Java 作为一门广泛使用的编程语言&#xff0c;它的基础知识是每个开发者必须掌握的。无论是面向对象编程&#xff08;OOP&#xff09;还是集合框架的使用&#xff0c;理解这些核心概念能够帮助我们在日常开发中更加高效和准确地编写代码。本文将从设计模式、集合原理到常见类的…

【C++习题】24.二分查找算法_0~n-1中缺失的数字

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 剑指 Offer 53 - II. 0&#xff5e;n-1中缺失的数字 题目描述&#xff1a; 解法 哈希表&#xff1a; 建立一个hash表看哪个数字出现次数为0 直接遍历找结果&#xff1…

(即插即用模块-Convolution部分) 一、(ICLR 2022) ODConv 全维动态卷积

文章目录 1、Omni-dimensional Dynamic Convolution2、代码实现 paper&#xff1a;OMNI-DIMENSIONAL DYNAMIC CONVOLUTION Code&#xff1a;https://github.com/OSVAI/ODConv 1、Omni-dimensional Dynamic Convolution 论文首先分析了现有动态卷积的局限性&#xff0c;论文指出…

深度学习Python基础(2)

二 数据处理 一般来说PyTorch中深度学习训练的流程是这样的&#xff1a; 1. 创建Dateset 2. Dataset传递给DataLoader 3. DataLoader迭代产生训练数据提供给模型 对应的一般都会有这三部分代码 # 创建Dateset(可以自定义) dataset face_dataset # Dataset部分自定义过的…

(超详细图文详情)Navicat 配置连接 Oracle

1、下载依赖文件 Oracle官网下载直链&#xff1a;https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html 夸克网盘下载&#xff08;oracle19c版本&#xff09;&#xff1a;https://pan.quark.cn/s/5061e690debc 官网下载选择对应 Oracle 版…

jdk各个版本介绍

Java Development Kit&#xff08;JDK&#xff09;是Java平台的核心组件&#xff0c;它包含了Java编程语言、Java虚拟机&#xff08;JVM&#xff09;、Java类库以及用于编译、调试和运行Java应用程序的工具。 JDK 1.0-1.4&#xff08;经典时代&#xff09; • JDK 1.0&#xff…

二分法篇——于上下边界的扭转压缩间,窥见正解辉映之光(1)

前言 二分法&#xff0c;这一看似简单却又充满哲理的算法&#xff0c;犹如一道精巧的数学之门&#xff0c;带领我们在问题的迷雾中找到清晰的道路。它的名字虽简单&#xff0c;却深藏着智慧的光辉。在科学的浩瀚星空中&#xff0c;二分法如一颗璀璨的星辰&#xff0c;指引着我们…

基于 FFmpeg/Scrcpy 框架构建的一款高性能的安卓设备投屏管理工具-供大家学习研究参考

支持的投屏方式有:USB,WIFIADB,OTG,投屏之前需要开启开发者选项里面的USB调试。 主要功能有: 1.支持单个或多个设备投屏。 2.支持键鼠操控。 3.支持文字输入。 4.支持共享剪切板(可复制粘贴电脑端文字到手机端,也可导出手机剪切板到电脑端)。 5.支持视频图片上传,可单…