Java的同步与线程安全是并发编程中至关重要的部分。在多线程环境下,确保数据的一致性和避免竞态条件(race condition)是程序设计的关键。
线程安全(Thread Safety)是指多线程环境下,多个线程访问同一个对象时,保证对象的状态和行为是正确的。线程安全的主要挑战是避免竞态条件,这种情况发生在多个线程同时访问和修改共享资源时,导致数据不一致或不可预测的行为。
同步方法是使用synchronized关键字声明的方法。当一个方法被声明为同步方法时,线程必须获得对象的内置锁(intrinsic lock),即监视器锁(monitor lock),才能执行该方法。这意味着同一时间只有一个线程可以执行这个同步方法,其他线程必须等待,直到持有锁的线程释放锁。
同步方法的声明很简单,只需在方法前加上synchronized关键字:
- public class SynchronizedExample {
- private int count = 0;
-
- public synchronized void increment() {
- count++;
- }
-
- public synchronized int getCount() {
- return count;
- }
- }
在上面的例子中,increment和getCount方法都是同步的。如果一个线程正在执行increment方法,其他线程将无法执行这两个同步方法中的任何一个,直到第一个线程完成increment方法并释放锁。
静态同步方法使用的是类级别的锁(Class Level Lock),而不是实例级别的锁。其声明方式如下:
- public class SynchronizedStaticExample {
- private static int count = 0;
-
- public static synchronized void increment() {
- count++;
- }
-
- public static synchronized int getCount() {
- return count;
- }
- }
静态同步方法对于类的所有实例都只有一个锁,因此同一时间只能有一个线程执行这些静态同步方法。
同步块是指在方法内部的一部分代码块前加上synchronized关键字。这种方式允许开发者更细粒度地控制同步范围,进而提高并发性能。
同步块通常使用对象实例作为锁:
- public class SynchronizedBlockExample {
- private final Object lock = new Object();
- private int count = 0;
-
- public void increment() {
- synchronized (lock) {
- count++;
- }
- }
-
- public int getCount() {
- synchronized (lock) {
- return count;
- }
- }
- }
在上面的例子中,我们创建了一个名为lock的对象,并在synchronized块中使用它来实现同步。这样做的好处是可以仅对需要同步的代码部分加锁,而不是对整个方法加锁,从而提升性能。
this作为锁有时,也可以直接使用this对象作为锁:
- public class SynchronizedThisExample {
- private int count = 0;
-
- public void increment() {
- synchronized (this) {
- count++;
- }
- }
-
- public int getCount() {
- synchronized (this) {
- return count;
- }
- }
- }
使用this作为锁的同步块,意味着同步块所包含的代码在执行时必须获得当前实例的锁。
java.util.concurrent包java.util.concurrent包提供了一组强大的工具,用于简化并发编程,并提高程序的性能和可维护性。
java.util.concurrent.locks包中提供了显式锁(Explicit Lock),如ReentrantLock,其功能比内置锁更加强大和灵活。
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- public class ReentrantLockExample {
- private final Lock lock = new ReentrantLock();
- private int count = 0;
-
- public void increment() {
- lock.lock();
- try {
- count++;
- } finally {
- lock.unlock();
- }
- }
-
- public int getCount() {
- lock.lock();
- try {
- return count;
- } finally {
- lock.unlock();
- }
- }
- }
使用显式锁时,必须在finally块中释放锁,以确保在任何情况下锁都会被正确释放。
java.util.concurrent.atomic包提供了一组原子变量类,如AtomicInteger,AtomicLong等,用于高效地实现线程安全的数值操作。
- import java.util.concurrent.atomic.AtomicInteger;
-
- public class AtomicExample {
- private final AtomicInteger count = new AtomicInteger(0);
-
- public void increment() {
- count.incrementAndGet();
- }
-
- public int getCount() {
- return count.get();
- }
- }
原子变量通过底层的CAS(Compare-And-Swap)操作实现无锁的线程安全,因此在高并发场景下性能更好。
java.util.concurrent包中提供了多个线程安全的集合类,如ConcurrentHashMap,CopyOnWriteArrayList等。这些集合类在内部实现了高效的并发控制,避免了传统集合类在多线程环境下的并发问题。
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.Map;
-
- public class ConcurrentHashMapExample {
- private final Map
map = new ConcurrentHashMap<>(); -
- public void put(String key, Integer value) {
- map.put(key, value);
- }
-
- public Integer get(String key) {
- return map.get(key);
- }
- }
ConcurrentHashMap通过分段锁(Segmented Locking)实现高并发性能,适用于需要频繁读写操作的场景。
线程池是管理和重用线程资源的重要工具,java.util.concurrent包提供了丰富的线程池实现,如ThreadPoolExecutor,ScheduledThreadPoolExecutor等。
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
-
- public class ThreadPoolExample {
- private final ExecutorService executor = Executors.newFixedThreadPool(10);
-
- public void submitTask(Runnable task) {
- executor.submit(task);
- }
-
- public void shutdown() {
- executor.shutdown();
- }
- }
线程池通过复用一组线程来执行多个任务,避免了频繁创建和销毁线程的开销,提高了程序的性能和稳定性。
java.util.concurrent包中还提供了多种同步辅助类,如CountDownLatch,CyclicBarrier,Semaphore等,用于协调线程之间的同步。
CountDownLatch是一种同步辅助类,用于让一个或多个线程等待一组操作完成。
- import java.util.concurrent.CountDownLatch;
-
- public class CountDownLatchExample {
- private final CountDownLatch latch = new CountDownLatch(3);
-
- public void performTask() throws InterruptedException {
- latch.await();
- // Perform some action after all tasks are done
- }
-
- public void completeTask() {
- latch.countDown();
- }
- }
CyclicBarrier是一种用于多个线程相互等待,直到所有线程都到达某个屏障点(Barrier)。
- import java.util.concurrent.CyclicBarrier;
-
- public class CyclicBarrierExample {
- private final CyclicBarrier barrier = new CyclicBarrier(3, () -> {
- // Action to be performed when all threads reach the barrier
- });
-
- public void performTask() throws Exception {
- barrier.await();
- // Perform some action after all threads reach the barrier
- }
- }
Semaphore是一种用于控制同时访问某特定资源的线程数量的机制。
- import java.util.concurrent.Semaphore;
-
- public class SemaphoreExample {
- private final Semaphore semaphore = new Semaphore(3);
-
- public void performTask() throws InterruptedException {
- semaphore.acquire();
- try {
- // Access shared resource
- } finally {
- semaphore.release();
- }
- }
- }
Future和Callable接口用于表示和处理异步计算的结果。
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.Future;
-
- public class FutureCallableExample {
- private final ExecutorService executor = Executors.newFixedThreadPool(10);
-
- public void performTask() throws ExecutionException, InterruptedException {
- Callable
task = () -> { - // Perform some computation
- return 42;
- };
-
- Future
future = executor.submit(task); - Integer result = future.get(); // Blocks until the computation is complete
- }
-
- public void shutdown() {
- executor.shutdown();
- }
- }
ForkJoinPool是专为任务拆分和合并而设计的线程池,适用于递归任务的并行处理。
- import java.util.concurrent.RecursiveTask;
- import java.util.concurrent.ForkJoinPool;
-
- public class ForkJoinExample {
- static class Fibonacci extends RecursiveTask
{ - final int n;
-
- Fibonacci(int n) {
- this.n = n;
- }
-
- @Override
- protected Integer compute() {
- if (n <= 1) {
- return n;
- }
- Fibonacci f1 = new Fibonacci(n - 1);
- f1.fork();
- Fibonacci f2 = new Fibonacci(n - 2);
- return f2.compute() + f1.join();
- }
- }
-
- public static void main(String[] args) {
- ForkJoinPool pool = new ForkJoinPool();
- Fibonacci task = new Fibonacci(10);
- int result = pool.invoke(task);
- System.out.println("Fibonacci number: " + result);
- }
- }
ForkJoinPool通过工作窃取算法(Work Stealing Algorithm)实现高效的并行计算。
同步方法和同步块是Java内置的同步机制,用于控制对共享资源的访问。而java.util.concurrent包提供了更高级和灵活的并发工具,包括显式锁、原子变量、并发集合、线程池、同步辅助类、Future和Callable以及ForkJoinPool等。这些工具极大地简化了并发编程,提高了程序的性能和稳定性。
