• Java同步与线程安全,同步方法、同步块和java.util.concurrent包的使用


    Java的同步与线程安全是并发编程中至关重要的部分。在多线程环境下,确保数据的一致性和避免竞态条件(race condition)是程序设计的关键。

    一、Java中的线程安全

    线程安全(Thread Safety)是指多线程环境下,多个线程访问同一个对象时,保证对象的状态和行为是正确的。线程安全的主要挑战是避免竞态条件,这种情况发生在多个线程同时访问和修改共享资源时,导致数据不一致或不可预测的行为。

    二、同步方法(Synchronized Methods)

    1. 什么是同步方法

    同步方法是使用synchronized关键字声明的方法。当一个方法被声明为同步方法时,线程必须获得对象的内置锁(intrinsic lock),即监视器锁(monitor lock),才能执行该方法。这意味着同一时间只有一个线程可以执行这个同步方法,其他线程必须等待,直到持有锁的线程释放锁。

    2. 同步方法的实现

    同步方法的声明很简单,只需在方法前加上synchronized关键字:

    1. public class SynchronizedExample {
    2. private int count = 0;
    3. public synchronized void increment() {
    4. count++;
    5. }
    6. public synchronized int getCount() {
    7. return count;
    8. }
    9. }

    在上面的例子中,incrementgetCount方法都是同步的。如果一个线程正在执行increment方法,其他线程将无法执行这两个同步方法中的任何一个,直到第一个线程完成increment方法并释放锁。

    3. 静态同步方法

    静态同步方法使用的是类级别的锁(Class Level Lock),而不是实例级别的锁。其声明方式如下:

    1. public class SynchronizedStaticExample {
    2. private static int count = 0;
    3. public static synchronized void increment() {
    4. count++;
    5. }
    6. public static synchronized int getCount() {
    7. return count;
    8. }
    9. }

    静态同步方法对于类的所有实例都只有一个锁,因此同一时间只能有一个线程执行这些静态同步方法。

    三、同步块(Synchronized Blocks)

    1. 什么是同步块

    同步块是指在方法内部的一部分代码块前加上synchronized关键字。这种方式允许开发者更细粒度地控制同步范围,进而提高并发性能。

    2. 同步块的实现

    同步块通常使用对象实例作为锁:

    1. public class SynchronizedBlockExample {
    2. private final Object lock = new Object();
    3. private int count = 0;
    4. public void increment() {
    5. synchronized (lock) {
    6. count++;
    7. }
    8. }
    9. public int getCount() {
    10. synchronized (lock) {
    11. return count;
    12. }
    13. }
    14. }

    在上面的例子中,我们创建了一个名为lock的对象,并在synchronized块中使用它来实现同步。这样做的好处是可以仅对需要同步的代码部分加锁,而不是对整个方法加锁,从而提升性能。

    3. 使用this作为锁

    有时,也可以直接使用this对象作为锁:

    1. public class SynchronizedThisExample {
    2. private int count = 0;
    3. public void increment() {
    4. synchronized (this) {
    5. count++;
    6. }
    7. }
    8. public int getCount() {
    9. synchronized (this) {
    10. return count;
    11. }
    12. }
    13. }

    使用this作为锁的同步块,意味着同步块所包含的代码在执行时必须获得当前实例的锁。

    四、java.util.concurrent

    java.util.concurrent包提供了一组强大的工具,用于简化并发编程,并提高程序的性能和可维护性。

    1. 锁(Locks)

    java.util.concurrent.locks包中提供了显式锁(Explicit Lock),如ReentrantLock,其功能比内置锁更加强大和灵活。

    1. import java.util.concurrent.locks.Lock;
    2. import java.util.concurrent.locks.ReentrantLock;
    3. public class ReentrantLockExample {
    4. private final Lock lock = new ReentrantLock();
    5. private int count = 0;
    6. public void increment() {
    7. lock.lock();
    8. try {
    9. count++;
    10. } finally {
    11. lock.unlock();
    12. }
    13. }
    14. public int getCount() {
    15. lock.lock();
    16. try {
    17. return count;
    18. } finally {
    19. lock.unlock();
    20. }
    21. }
    22. }

    使用显式锁时,必须在finally块中释放锁,以确保在任何情况下锁都会被正确释放。

    2. 原子变量(Atomic Variables)

    java.util.concurrent.atomic包提供了一组原子变量类,如AtomicIntegerAtomicLong等,用于高效地实现线程安全的数值操作。

    1. import java.util.concurrent.atomic.AtomicInteger;
    2. public class AtomicExample {
    3. private final AtomicInteger count = new AtomicInteger(0);
    4. public void increment() {
    5. count.incrementAndGet();
    6. }
    7. public int getCount() {
    8. return count.get();
    9. }
    10. }

    原子变量通过底层的CAS(Compare-And-Swap)操作实现无锁的线程安全,因此在高并发场景下性能更好。

    3. 并发集合(Concurrent Collections)

    java.util.concurrent包中提供了多个线程安全的集合类,如ConcurrentHashMapCopyOnWriteArrayList等。这些集合类在内部实现了高效的并发控制,避免了传统集合类在多线程环境下的并发问题。

    1. import java.util.concurrent.ConcurrentHashMap;
    2. import java.util.Map;
    3. public class ConcurrentHashMapExample {
    4. private final Map map = new ConcurrentHashMap<>();
    5. public void put(String key, Integer value) {
    6. map.put(key, value);
    7. }
    8. public Integer get(String key) {
    9. return map.get(key);
    10. }
    11. }

    ConcurrentHashMap通过分段锁(Segmented Locking)实现高并发性能,适用于需要频繁读写操作的场景。

    4. 线程池(Thread Pools)

    线程池是管理和重用线程资源的重要工具,java.util.concurrent包提供了丰富的线程池实现,如ThreadPoolExecutorScheduledThreadPoolExecutor等。

    1. import java.util.concurrent.ExecutorService;
    2. import java.util.concurrent.Executors;
    3. public class ThreadPoolExample {
    4. private final ExecutorService executor = Executors.newFixedThreadPool(10);
    5. public void submitTask(Runnable task) {
    6. executor.submit(task);
    7. }
    8. public void shutdown() {
    9. executor.shutdown();
    10. }
    11. }

    线程池通过复用一组线程来执行多个任务,避免了频繁创建和销毁线程的开销,提高了程序的性能和稳定性。

    5. 同步辅助类(Synchronizers)

    java.util.concurrent包中还提供了多种同步辅助类,如CountDownLatchCyclicBarrierSemaphore等,用于协调线程之间的同步。

    CountDownLatch

    CountDownLatch是一种同步辅助类,用于让一个或多个线程等待一组操作完成。

    1. import java.util.concurrent.CountDownLatch;
    2. public class CountDownLatchExample {
    3. private final CountDownLatch latch = new CountDownLatch(3);
    4. public void performTask() throws InterruptedException {
    5. latch.await();
    6. // Perform some action after all tasks are done
    7. }
    8. public void completeTask() {
    9. latch.countDown();
    10. }
    11. }
    CyclicBarrier

    CyclicBarrier是一种用于多个线程相互等待,直到所有线程都到达某个屏障点(Barrier)。

    1. import java.util.concurrent.CyclicBarrier;
    2. public class CyclicBarrierExample {
    3. private final CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    4. // Action to be performed when all threads reach the barrier
    5. });
    6. public void performTask() throws Exception {
    7. barrier.await();
    8. // Perform some action after all threads reach the barrier
    9. }
    10. }
    Semaphore

    Semaphore是一种用于控制同时访问某特定资源的线程数量的机制。

    1. import java.util.concurrent.Semaphore;
    2. public class SemaphoreExample {
    3. private final Semaphore semaphore = new Semaphore(3);
    4. public void performTask() throws InterruptedException {
    5. semaphore.acquire();
    6. try {
    7. // Access shared resource
    8. } finally {
    9. semaphore.release();
    10. }
    11. }
    12. }

    6. Future和Callable

    FutureCallable接口用于表示和处理异步计算的结果。

    1. import java.util.concurrent.Callable;
    2. import java.util.concurrent.ExecutionException;
    3. import java.util.concurrent.ExecutorService;
    4. import java.util.concurrent.Executors;
    5. import java.util.concurrent.Future;
    6. public class FutureCallableExample {
    7. private final ExecutorService executor = Executors.newFixedThreadPool(10);
    8. public void performTask() throws ExecutionException, InterruptedException {
    9. Callable task = () -> {
    10. // Perform some computation
    11. return 42;
    12. };
    13. Future future = executor.submit(task);
    14. Integer result = future.get(); // Blocks until the computation is complete
    15. }
    16. public void shutdown() {
    17. executor.shutdown();
    18. }
    19. }

    7. ForkJoinPool

    ForkJoinPool是专为任务拆分和合并而设计的线程池,适用于递归任务的并行处理。

    1. import java.util.concurrent.RecursiveTask;
    2. import java.util.concurrent.ForkJoinPool;
    3. public class ForkJoinExample {
    4. static class Fibonacci extends RecursiveTask {
    5. final int n;
    6. Fibonacci(int n) {
    7. this.n = n;
    8. }
    9. @Override
    10. protected Integer compute() {
    11. if (n <= 1) {
    12. return n;
    13. }
    14. Fibonacci f1 = new Fibonacci(n - 1);
    15. f1.fork();
    16. Fibonacci f2 = new Fibonacci(n - 2);
    17. return f2.compute() + f1.join();
    18. }
    19. }
    20. public static void main(String[] args) {
    21. ForkJoinPool pool = new ForkJoinPool();
    22. Fibonacci task = new Fibonacci(10);
    23. int result = pool.invoke(task);
    24. System.out.println("Fibonacci number: " + result);
    25. }
    26. }

    ForkJoinPool通过工作窃取算法(Work Stealing Algorithm)实现高效的并行计算。

    同步方法和同步块是Java内置的同步机制,用于控制对共享资源的访问。而java.util.concurrent包提供了更高级和灵活的并发工具,包括显式锁、原子变量、并发集合、线程池、同步辅助类、Future和Callable以及ForkJoinPool等。这些工具极大地简化了并发编程,提高了程序的性能和稳定性。

    黑马程序员免费预约咨询

  • 相关阅读:
    React + Taro 项目 实际书写 感受
    ansible的介绍安装与模块
    leetcode 57. 插入区间
    Datawhale 202208 GitModel |线性规划 整数规划 多目标规划 灵敏度分析
    Docker安装Python3教程
    Docker使用遇到问题:docker build requires exactly 1 argument(s)
    海外版知乎Quora,如何使用Quora进行营销?
    【暴力剪枝】CF1708D
    【论文阅读|深读】SDNE:Structural Deep Network Embedding
    【JavaScript】JS执行机制--同步与异步
  • 原文地址:https://blog.csdn.net/Itmastergo/article/details/139462994