Java中的并发编程类

一.总体介绍

基础的线程Thread类,Runnable接口以及高级一点的Future,Callable接口。到后来的线程池ThreadPoolExecutor类,以及一些常用的volatile,synchronized关键字,原子类,通信工具类,还有一些并发集合ConcurrentHashMapCopyOnWriteArrayListCopyOnWriteArraySetConcurrentLinkedQueueBlockingQueue等等内容。今天博主为大家好好梳理一下这些内容,好啦开始吧。

二.基础类

基础类主要用于创建线程,比如Thread,Runnable,Future,Callable都可以实现。

  • Thread  ---------    只需要重写run()即可
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running");
    }
}

public class Demo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start(); // 注意:必须用 start() 启动线程
    }
}

不过存在一个缺点,就是Java是不支持多继承的,所以继承了Thread类,就不可以继承其他类了,可扩展性有限。

  • Runnable   -----对上面的优化  因为实现的是接口。
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable is running");
    }
}

public class Demo {
    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread t1 = new Thread(r);
        t1.start();
    }
}

但是存在一个问题就是Runnable不支持返回值

run() 方法不能抛出受检异常(checked exception)。如果任务中可能抛出异常,必须在方法内部捕获并处理。

  • Future    ------ 异步的,同样也是接口  一般与线程池配合使用
ExecutorService executor = Executors.newFixedThreadPool(1);//就是一个固定大小的线程池

Future future = executor.submit(() -> {
    Thread.sleep(1000);
    return 42;
});

System.out.println("Doing other things...");
System.out.println("Result: " + future.get());  // 阻塞直到拿到结果
executor.shutdown();
  • Callable     ------  相较于Runnable而言支持返回值 同样支持check exception可以throws xxx
class MyCallable implements Callable {
    @Override
    public Integer call() throws Exception {
        Thread.sleep(1000);
        return 100;
    }
}

public class Demo {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();//不会并发执行,线程池中只有一个线程
        Future future = executor.submit(new MyCallable());
        System.out.println("Result: " + future.get());
        executor.shutdown();
    }
}

三.线程池

ThreadPoolExecutor(面试重点)

首先说说咋用,得知道咋设置参数吧。有七大参数。

1. corePoolSize 核心线程数  CPU密集型:一般设置为CPU核数(+1)

                                                IO密集型: 一般为CPU核数的2倍

  • 含义:线程池中一直保留的线程数量,即使它们是空闲的,也不会被回收(除非设置了特殊参数)。

  • 作用:提前准备好一定数量的线程,处理常规负载。

2. maximumPoolSize 最大线程数

  • 含义:线程池中允许存在的最大线程数量

  • 作用:当任务量激增时,可以临时创建更多线程,但不超过这个最大值。

3. keepAliveTime 线程空闲存活时间

  • 含义:当线程数量超过核心线程数时,多余的线程在空闲多久后会被销毁。

  • 作用:防止临时增加的线程无限制地存在,浪费资源。

  • 举例keepAliveTime = 60 秒,说明额外的线程空闲超过60秒就会被回收。

4. unit 时间单位

  • 含义:配合 keepAliveTime 使用,指定时间的单位。

  • 常用值TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)、TimeUnit.MINUTES(分钟)等。

5. workQueue 任务队列

  • 含义:用于缓存等待执行的任务。

  • 作用:当核心线程满了,新的任务就会放进这个队列排队。

  • 常见实现

    • ArrayBlockingQueue:有界队列,固定大小,常用。

    • LinkedBlockingQueue:无界队列(实际上是很大),任务堆积多时容易OOM。

    • SynchronousQueue:不存储元素,提交任务必须直接交给线程处理(用于任务数量多、处理快的场景)。

  • 常见的阻塞队列

    • 有界队列ArrayBlockingQueue 数组实现  有界的先进先出的队列 
    • public class ArrayBlockingQueue extends AbstractQueue 
              implements BlockingQueue, java.io.Serializable {
          private final Object[] items;
          private int takeIndex;          // 取出元素的索引
          private int putIndex;           // 插入元素的索引
          private int count;              // 当前队列中元素的数量
      
          private final ReentrantLock lock = new ReentrantLock();  // 锁
          private final Condition notEmpty = lock.newCondition();  // 空队列条件
          private final Condition notFull = lock.newCondition();   // 满队列条件
      
          // 构造方法,指定容量
          public ArrayBlockingQueue(int capacity) {
              if (capacity <= 0) throw new IllegalArgumentException();
              this.items = new Object[capacity];
          }
      
          @Override
          public void put(E e) throws InterruptedException {
              // 等待直到队列不满
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  while (count == items.length)
                      notFull.await();
                  enqueue(e);
              } finally {
                  lock.unlock();
              }
          }
      
          private void enqueue(E e) {
              items[putIndex] = e;
              if (++putIndex == items.length) putIndex = 0;
              ++count;
              notEmpty.signal();
          }
      
          @Override
          public E take() throws InterruptedException {
              // 等待直到队列不空
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  while (count == 0)
                      notEmpty.await();
                  return dequeue();
              } finally {
                  lock.unlock();
              }
          }
      
          private E dequeue() {
              @SuppressWarnings("unchecked")
              E x = (E) items[takeIndex];
              items[takeIndex] = null;
              if (++takeIndex == items.length) takeIndex = 0;
              --count;
              notFull.signal();
              return x;
          }
      
          // 其他方法略...
      }
      

    • 无界队列LinkedBlockingQueue  链表实现 默认Integer.MAX_VALUE 无界队列
    • public class LinkedBlockingQueue extends AbstractQueue 
              implements BlockingQueue, java.io.Serializable {
          private final Node head;    // 队列头
          private Node last;          // 队列尾部
          private final ReentrantLock lock = new ReentrantLock();  // 锁
          private final Condition notEmpty = lock.newCondition();  // 空队列条件
          private final Condition notFull = lock.newCondition();   // 满队列条件
          private int count;             // 当前队列中元素的数量
          private final int capacity;    // 队列最大容量
      
          public LinkedBlockingQueue(int capacity) {
              if (capacity <= 0) throw new IllegalArgumentException();
              this.capacity = capacity;
              this.head = new Node(null);
              this.last = head;
          }
      
          @Override
          public void put(E e) throws InterruptedException {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  while (count == capacity)
                      notFull.await();
                  enqueue(e);
              } finally {
                  lock.unlock();
              }
          }
      
          private void enqueue(E e) {
              Node node = new Node(e);
              last.next = node;
              last = node;
              ++count;
              notEmpty.signal();
          }
      
          @Override
          public E take() throws InterruptedException {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  while (count == 0)
                      notEmpty.await();
                  return dequeue();
              } finally {
                  lock.unlock();
              }
          }
      
          private E dequeue() {
              Node first = head.next;
              head.next = first.next;
              if (first == last)
                  last = head;
              --count;
              notFull.signal();
              return first.item;
          }
      
          // 其他方法略...
      }
      

    • 优先级队列PriorityBlockingQueue  支持优先级  可以按照Comparator来排序
    • public class PriorityBlockingQueue extends AbstractQueue 
              implements BlockingQueue, java.io.Serializable {
          private final Comparator comparator;  // 优先级比较器
          private final PriorityQueue queue;
      
          public PriorityBlockingQueue(int initialCapacity, Comparator comparator) {
              this.queue = new PriorityQueue(initialCapacity, comparator);
              this.comparator = comparator;
          }
      
          @Override
          public void put(E e) throws InterruptedException {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  queue.add(e);  // 使用PriorityQueue的add方法按优先级插入
              } finally {
                  lock.unlock();
              }
          }
      
          @Override
          public E take() throws InterruptedException {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  if (queue.isEmpty()) {
                      return null;  // 队列为空返回null
                  }
                  return queue.poll();  // 按优先级取出元素
              } finally {
                  lock.unlock();
              }
          }
      
          // 其他方法略...
      }
      
    • 延迟队列DelayQueue   由二叉堆实现的无界阻塞队列
    • package java.util.concurrent;
      
      import java.util.*;
      import java.util.concurrent.locks.Condition;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class DelayQueue extends AbstractQueue
              implements BlockingQueue {
      
          private final transient ReentrantLock lock = new ReentrantLock();
          private final PriorityQueue q = new PriorityQueue<>();
          private Thread leader = null;
          private final Condition available = lock.newCondition();
      
          public DelayQueue() {}
      
          public DelayQueue(Collection c) {
              this.addAll(c);
          }
      
          @Override
          public boolean add(E e) {
              return offer(e);
          }
      
          @Override
          public boolean offer(E e) {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  q.offer(e);
                  if (q.peek() == e) {
                      leader = null;
                      available.signal();
                  }
                  return true;
              } finally {
                  lock.unlock();
              }
          }
      
          @Override
          public void put(E e) {
              offer(e);
          }
      
          @Override
          public boolean offer(E e, long timeout, TimeUnit unit) {
              return offer(e);
          }
      
          @Override
          public E poll() {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  E first = q.peek();
                  if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
                      return null;
                  else
                      return q.poll();
              } finally {
                  lock.unlock();
              }
          }
      
          @Override
          public E take() throws InterruptedException {
              final ReentrantLock lock = this.lock;
              lock.lockInterruptibly();
              try {
                  for (;;) {
                      E first = q.peek();
                      if (first == null)
                          available.await();
                      else {
                          long delay = first.getDelay(TimeUnit.NANOSECONDS);
                          if (delay <= 0)
                              return q.poll();
                          first = null; // 不要保留引用
                          if (leader != null)
                              available.await();
                          else {
                              Thread thisThread = Thread.currentThread();
                              leader = thisThread;
                              try {
                                  available.awaitNanos(delay);
                              } finally {
                                  if (leader == thisThread)
                                      leader = null;
                              }
                          }
                      }
                  }
              } finally {
                  if (leader == null && q.peek() != null)
                      available.signal();
                  lock.unlock();
              }
          }
      
          @Override
          public E peek() {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  return q.peek();
              } finally {
                  lock.unlock();
              }
          }
      
          @Override
          public int size() {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  return q.size();
              } finally {
                  lock.unlock();
              }
          }
      
          @Override
          public int drainTo(Collection c) {
              return drainTo(c, Integer.MAX_VALUE);
          }
      
          @Override
          public int drainTo(Collection c, int maxElements) {
              if (c == null)
                  throw new NullPointerException();
              if (c == this)
                  throw new IllegalArgumentException();
              if (maxElements <= 0)
                  return 0;
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  int n = 0;
                  while (n < maxElements) {
                      E first = q.peek();
                      if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
                          break;
                      c.add(q.poll());
                      ++n;
                  }
                  return n;
              } finally {
                  lock.unlock();
              }
          }
      
          @Override
          public Iterator iterator() {
              return new Itr(toArray());
          }
      
          private Object[] toArray() {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  return q.toArray();
              } finally {
                  lock.unlock();
              }
          }
      
          private class Itr implements Iterator {
              final Object[] array; // Array of all elements
              int cursor;           // Index of next element to return
              int lastRet;          // Index of last element, or -1 if none
      
              Itr(Object[] array) {
                  this.array = array;
                  lastRet = -1;
              }
      
              @Override
              public boolean hasNext() {
                  return cursor < array.length;
              }
      
              @Override
              @SuppressWarnings("unchecked")
              public E next() {
                  if (cursor >= array.length)
                      throw new NoSuchElementException();
                  lastRet = cursor;
                  return (E) array[cursor++];
              }
      
              @Override
              public void remove() {
                  if (lastRet < 0)
                      throw new IllegalStateException();
                  Object x = array[lastRet];
                  lastRet = -1;
                  final ReentrantLock lock = DelayQueue.this.lock;
                  lock.lock();
                  try {
                      for (Iterator it = q.iterator(); it.hasNext(); ) {
                          if (it.next() == x) {
                              it.remove();
                              break;
                          }
                      }
                  } finally {
                      lock.unlock();
                  }
              }
          }
      }

    • 同步队列SynchronousQueue  每个插入操作不许等代另一个线程的移除操作
  • package java.util.concurrent;
    
    import java.util.*;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class DelayQueue extends AbstractQueue
            implements BlockingQueue {
    
        private final transient ReentrantLock lock = new ReentrantLock();
        private final PriorityQueue q = new PriorityQueue<>();
        private Thread leader = null;
        private final Condition available = lock.newCondition();
    
        public DelayQueue() {}
    
        public DelayQueue(Collection c) {
            this.addAll(c);
        }
    
        @Override
        public boolean add(E e) {
            return offer(e);
        }
    
        @Override
        public boolean offer(E e) {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                q.offer(e);
                if (q.peek() == e) {
                    leader = null;
                    available.signal();
                }
                return true;
            } finally {
                lock.unlock();
            }
        }
    
        @Override
        public void put(E e) {
            offer(e);
        }
    
        @Override
        public boolean offer(E e, long timeout, TimeUnit unit) {
            return offer(e);
        }
    
        @Override
        public E poll() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                E first = q.peek();
                if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
                    return null;
                else
                    return q.poll();
            } finally {
                lock.unlock();
            }
        }
    
        @Override
        public E take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                for (;;) {
                    E first = q.peek();
                    if (first == null)
                        available.await();
                    else {
                        long delay = first.getDelay(TimeUnit.NANOSECONDS);
                        if (delay <= 0)
                            return q.poll();
                        first = null; // 不要保留引用
                        if (leader != null)
                            available.await();
                        else {
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
                if (leader == null && q.peek() != null)
                    available.signal();
                lock.unlock();
            }
        }
    
        @Override
        public E peek() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                return q.peek();
            } finally {
                lock.unlock();
            }
        }
    
        @Override
        public int size() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                return q.size();
            } finally {
                lock.unlock();
            }
        }
    
        @Override
        public int drainTo(Collection c) {
            return drainTo(c, Integer.MAX_VALUE);
        }
    
        @Override
        public int drainTo(Collection c, int maxElements) {
            if (c == null)
                throw new NullPointerException();
            if (c == this)
                throw new IllegalArgumentException();
            if (maxElements <= 0)
                return 0;
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                int n = 0;
                while (n < maxElements) {
                    E first = q.peek();
                    if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
                        break;
                    c.add(q.poll());
                    ++n;
                }
                return n;
            } finally {
                lock.unlock();
            }
        }
    
        @Override
        public Iterator iterator() {
            return new Itr(toArray());
        }
    
        private Object[] toArray() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                return q.toArray();
            } finally {
                lock.unlock();
            }
        }
    
        private class Itr implements Iterator {
            final Object[] array; // Array of all elements
            int cursor;           // Index of next element to return
            int lastRet;          // Index of last element, or -1 if none
    
            Itr(Object[] array) {
                this.array = array;
                lastRet = -1;
            }
    
            @Override
            public boolean hasNext() {
                return cursor < array.length;
            }
    
            @Override
            @SuppressWarnings("unchecked")
            public E next() {
                if (cursor >= array.length)
                    throw new NoSuchElementException();
                lastRet = cursor;
                return (E) array[cursor++];
            }
    
            @Override
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                Object x = array[lastRet];
                lastRet = -1;
                final ReentrantLock lock = DelayQueue.this.lock;
                lock.lock();
                try {
                    for (Iterator it = q.iterator(); it.hasNext(); ) {
                        if (it.next() == x) {
                            it.remove();
                            break;
                        }
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    6. threadFactory 线程工厂

  • 含义:用来定制线程的创建方式,比如给线程起名字、设置优先级、是否守护线程等。

  • 作用:让线程更容易管理和排查,比如异常时能快速定位是哪条线程。

  • 默认实现Executors.defaultThreadFactory()

  • 举例:可以自己实现一个 ThreadFactory,给线程设置统一的前缀名字。

7. handler 拒绝策略

  • 含义:当线程池满了(线程数=最大线程数,且任务队列也满了),新任务怎么办

  • 常见策略

    • AbortPolicy(默认):直接抛异常(RejectedExecutionException)。

    • CallerRunsPolicy:交给提交任务的线程自己执行(让主线程去跑任务)。

    • DiscardPolicy:直接丢弃任务,不抛异常。

    • DiscardOldestPolicy:丢掉最老的任务(队列头部),然后尝试重新提交新任务。

简单的使用:

import java.util.concurrent.*;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        // 自定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, // 核心线程数
            4, // 最大线程数
            60, // 空闲线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new LinkedBlockingQueue<>(2) // 任务队列容量为 2
        );

        // 提交任务
        for (int i = 1; i <= 6; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}

常见的线程池:

newFixedThreadPool()固定大小  数据库连接池

newCachedThreadPool()缓存线程池     短时间内存在大量文件处理或网络请求

newScheduledThreadPool()定时任务   定时发送邮件,定时备份数据

newSingleThreadExecutor()单线程池  日志记录    文件处理

线程池处理异常:

1. try-catch 异常处理

示例:
try {
    int result = 10 / 0;  // 会抛出ArithmeticException
} catch (ArithmeticException e) {
    System.out.println("发生了除零错误: " + e.getMessage());
} finally {
    System.out.println("无论如何都会执行");
}

2. Future 接口

Future 是 Java 中的一个接口,代表一个异步计算的结果。它通常用于表示任务的结果,并提供方法来检查任务的状态、取消任务或获取任务的执行结果。

  • get():获取任务的结果,若任务未完成则会阻塞。

  • cancel():尝试取消任务的执行。

  • isDone():检查任务是否完成。

  • isCancelled():检查任务是否被取消。

示例:
ExecutorService executor = Executors.newFixedThreadPool(1);
Future future = executor.submit(() -> {
    // 模拟耗时操作
    Thread.sleep(1000);
    return 42;
});

try {
    Integer result = future.get();  // 阻塞,直到任务完成
    System.out.println("任务的结果是: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
} finally {
    executor.shutdown();
}
  • 注意:调用 get() 会阻塞,直到 Future 对象代表的任务完成。

3. 自定义 afterExecute 方法

afterExecuteThreadPoolExecutor 类的一个方法,可以在任务执行完成后执行一些操作。它通常用于日志记录、清理资源等操作。

示例:
class MyThreadPoolExecutor extends ThreadPoolExecutor {
    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                BlockingQueue workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t != null) {
            System.out.println("任务执行时发生了异常: " + t.getMessage());
        } else {
            System.out.println("任务执行成功!");
        }
    }
}

public class Example {
    public static void main(String[] args) {
        MyThreadPoolExecutor executor = new MyThreadPoolExecutor(
            1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

        executor.submit(() -> {
            // 执行的任务
            System.out.println("任务开始执行");
        });
        
        executor.shutdown();
    }
}
  • 在上面的代码中,afterExecute 会在每个任务执行结束后被调用,用于处理任务执行后的操作,如异常处理或日志记录。

4. UncaughtExceptionHandler 捕获异常

UncaughtExceptionHandler 是 Java 中的一个接口,用于处理线程未捕获的异常。当线程中的异常没有被捕获时,UncaughtExceptionHandler 可以提供全局处理机制。

设置 UncaughtExceptionHandler

你可以为每个线程设置一个 UncaughtExceptionHandler,或者使用全局的默认处理器。

public class Example {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
            System.out.println("捕获到未处理的异常: " + throwable.getMessage());
        });

        Thread thread = new Thread(() -> {
            // 模拟抛出异常
            throw new RuntimeException("任务执行失败");
        });

        thread.start();
    }
}

在上面的代码中,Thread.setDefaultUncaughtExceptionHandler() 会设置一个全局的未捕获异常处理器,捕获到任何线程中的未处理异常时,都会触发该处理器。

  • 你也可以为每个线程单独设置 UncaughtExceptionHandler,如下所示:

Thread thread = new Thread(() -> {
    // 模拟抛出异常
    throw new RuntimeException("任务执行失败");
});
thread.setUncaughtExceptionHandler((t, e) -> {
    System.out.println("捕获到线程 " + t.getName() + " 的异常: " + e.getMessage());
});
thread.start();
  • 该处理器会在线程抛出未捕获异常时执行,用于日志记录、清理资源等。

小结

  • try-catch 用于捕获和处理异常。

  • Future 用于获取异步任务的执行结果,提供了一些方法来检查任务的状态、获取结果、取消任务。

  • afterExecuteThreadPoolExecutor 中的一个方法,可以在任务完成后执行某些操作,比如日志记录、清理资源等。

  • UncaughtExceptionHandler 用于处理未捕获的线程异常,可以通过设置全局或线程级的异常处理器来捕获和处理这些异常。

线程池的状态:Runnable--->SHUTDOWN---->STOP---->TIDYING---->TERMINATED

四.重要关键字

1. volatile

作用
  • volatile 是一个轻量级的同步机制,用于确保变量的可见性和禁止指令重排序。
  • 它适用于单个变量的读写操作。
核心特性
  1. 可见性

    当一个线程修改了 volatile 修饰的变量时,其他线程可以立即看到最新的值。这是因为 volatile 变量会直接从主内存中读取,而不是从线程的本地缓存中读取。
  2. 禁止指令重排序

    JVM 在优化代码时可能会对指令进行重排序,而 volatile 能够防止这种重排序,从而保证程序的执行顺序符合预期。
  3. 不保证原子性

    volatile 不能保证复合操作(如 i++)的原子性。如果需要原子性操作,可以配合 synchronized 或使用 Atomic 类。
适用场景
  • 状态标志
    • 用于控制线程的启动、停止或暂停。
    • 示例:通过一个 volatile 标志位控制线程运行。
class VolatileExample {
    private volatile boolean running = true;

    public void stop() {
        running = false;
    }

    public void run() {
        while (running) {
            // 执行任务
        }
        System.out.println("Thread stopped");
    }
}
  • 共享变量
    • 用于多个线程共享的简单变量(如计数器),但需要注意它不保证原子性。
优点
  • 比 synchronized 更轻量级,性能开销小。
  • 适合简单的同步需求。
缺点
  • 不支持复杂的同步逻辑(如互斥锁、条件等待等)。
  • 无法保证复合操作的原子性。

2. synchronized(博主在前一篇文章详细讨论了Java中的锁,感兴趣的可以去看看)

作用
  • synchronized 是一种重量级的同步机制,用于确保同一时刻只有一个线程可以访问某个资源。
  • 它提供了 互斥锁 的功能,能够保证线程安全。
核心特性
  1. 互斥性

    同一时刻只有一个线程可以持有锁,其他线程必须等待锁释放后才能进入。
  2. 可见性

    线程在进入 synchronized 块时会刷新本地缓存,退出时会将修改后的值写回主内存,从而保证变量的可见性。
  3. 原子性

    synchronized 能够保证代码块或方法中的操作是原子性的,避免多线程并发导致的数据不一致问题。
  4. 可重入性

    同一个线程可以多次获取同一个锁,而不会发生死锁。
使用方式
  • 同步方法:使用 synchronized 修饰方法,锁住整个方法。
public synchronized void increment() {
    count++;
}
  • 同步代码块:使用 synchronized 锁定特定的对象或代码块。
public void increment() {
    synchronized (this) {
        count++;
    }
}
适用场景
  • 复杂同步逻辑
    • 需要保护多个共享变量或执行复合操作(如 i++)。
    • 示例:银行账户余额的增减操作。
class BankAccount {
    private int balance;

    public synchronized void deposit(int amount) {
        balance += amount;
    }

    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
        } else {
            System.out.println("Insufficient balance");
        }
    }
}
  • 临界区保护
    • 保护某些关键代码段,确保同一时刻只有一个线程可以执行。
优点
  • 提供完整的线程安全保护,包括可见性、原子性和互斥性。
  • 支持复杂的同步逻辑。
缺点
  • 性能开销较大,尤其是在高并发场景下。
  • 容易引发死锁问题,需要小心设计锁的使用。

3. volatile 和 synchronized 的对比

特性 volatile synchronized
作用范围 单个变量 方法或代码块
可见性 保证变量的可见性 保证变量的可见性
原子性 不保证复合操作的原子性 保证操作的原子性
互斥性 不提供互斥锁 提供互斥锁
性能开销 较低 较高
适用场景 简单的状态标志或共享变量 复杂的同步逻辑或临界区保护

五.并发集合类

这里重点介绍ConcurrentHashMap,是HashMap的线程安全版本。

对于读操作:通过volatile实现线程间内存变量的可见性

对于写操作:JDK7前通过分段所实现,JDK8后通过CAS+synchronized实现。

JDK7:对于完整的Map会被分为若干段,每个端都可以独立地加锁,内部维护一个HashEntry table。软继承了ReentrantLock,不同线程可以同时操作不同的段,实现并发。

JDK8:先尝试通过CAS加锁,失败后重试,重试超过一定过的次数,直接通过synchronized加锁。(仅在不要使加锁)

    public void lock() {
        int spins = 0;

        // 尝试通过 CAS 获取锁
        while (!state.compareAndSet(0, 1)) {
            if (++spins > MAX_SPINS) {
                // 如果自旋次数超过阈值,回退到 synchronized 加锁
                synchronized (this) {
                    try {
                        // 确保锁被释放后再次尝试获取
                        while (state.get() != 0) {
                            this.wait(); // 等待锁释放
                        }
                        state.set(1); // 获取锁
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    return;
                }
            }
        }
    }

    public void unlock() {
        if (state.get() == 1) {
            state.set(0); // 释放锁
            synchronized (this) {
                this.notifyAll(); // 唤醒等待的线程
            }
        }
    }

好啦这篇文章先写到这里,以后还会继续补充并发工具类以及其他内容的。如果由其他想看的内容也可以告诉博主哦。

感谢你看到这里,喜欢的话可以点点关注哦!

你可能感兴趣的:(java,开发语言)