Java的同步与线程安全是并发编程中至关重要的部分。在多线程环境下,确保数据的一致性和避免竞态条件(race condition)是程序设计的关键。
一、Java中的线程安全
线程安全(Thread Safety)是指多线程环境下,多个线程访问同一个对象时,保证对象的状态和行为是正确的。线程安全的主要挑战是避免竞态条件,这种情况发生在多个线程同时访问和修改共享资源时,导致数据不一致或不可预测的行为。
二、同步方法(Synchronized Methods)
1. 什么是同步方法
同步方法是使用synchronized
关键字声明的方法。当一个方法被声明为同步方法时,线程必须获得对象的内置锁(intrinsic lock),即监视器锁(monitor lock),才能执行该方法。这意味着同一时间只有一个线程可以执行这个同步方法,其他线程必须等待,直到持有锁的线程释放锁。
2. 同步方法的实现
同步方法的声明很简单,只需在方法前加上synchronized
关键字:
public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
在上面的例子中,increment
和getCount
方法都是同步的。如果一个线程正在执行increment
方法,其他线程将无法执行这两个同步方法中的任何一个,直到第一个线程完成increment
方法并释放锁。
3. 静态同步方法
静态同步方法使用的是类级别的锁(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 Blocks)
1. 什么是同步块
同步块是指在方法内部的一部分代码块前加上synchronized
关键字。这种方式允许开发者更细粒度地控制同步范围,进而提高并发性能。
2. 同步块的实现
同步块通常使用对象实例作为锁:
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
块中使用它来实现同步。这样做的好处是可以仅对需要同步的代码部分加锁,而不是对整个方法加锁,从而提升性能。
3. 使用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
包提供了一组强大的工具,用于简化并发编程,并提高程序的性能和可维护性。
1. 锁(Locks)
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
块中释放锁,以确保在任何情况下锁都会被正确释放。
2. 原子变量(Atomic Variables)
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)操作实现无锁的线程安全,因此在高并发场景下性能更好。
3. 并发集合(Concurrent Collections)
java.util.concurrent
包中提供了多个线程安全的集合类,如ConcurrentHashMap
,CopyOnWriteArrayList
等。这些集合类在内部实现了高效的并发控制,避免了传统集合类在多线程环境下的并发问题。
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;public class ConcurrentHashMapExample {private final Map<String, Integer> 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)实现高并发性能,适用于需要频繁读写操作的场景。
4. 线程池(Thread Pools)
线程池是管理和重用线程资源的重要工具,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();}
}
线程池通过复用一组线程来执行多个任务,避免了频繁创建和销毁线程的开销,提高了程序的性能和稳定性。
5. 同步辅助类(Synchronizers)
java.util.concurrent
包中还提供了多种同步辅助类,如CountDownLatch
,CyclicBarrier
,Semaphore
等,用于协调线程之间的同步。
CountDownLatch
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
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
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();}}
}
6. Future和Callable
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<Integer> task = () -> {// Perform some computationreturn 42;};Future<Integer> future = executor.submit(task);Integer result = future.get(); // Blocks until the computation is complete}public void shutdown() {executor.shutdown();}
}
7. ForkJoinPool
ForkJoinPool
是专为任务拆分和合并而设计的线程池,适用于递归任务的并行处理。
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;public class ForkJoinExample {static class Fibonacci extends RecursiveTask<Integer> {final int n;Fibonacci(int n) {this.n = n;}@Overrideprotected 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等。这些工具极大地简化了并发编程,提高了程序的性能和稳定性。
黑马程序员免费预约咨询