Java JUC(Java Util Concurrent)是 Java 平台提供的并发编程工具包,它提供了一系列的工具类和接口,用于简化多线程编程。JUC 中的类和接口都是基于 Java 平台的底层并发原语(如锁、信号量、原子变量等)实现的,可以帮助开发者更加方便和安全地完成多线程编程。
JUC 中的常用类和接口
1. Lock 和 ReentrantLock
Lock 接口是 Java 并发包中提供的一种比 synchronized 更加灵活的锁机制。它提供了更加细粒度的控制,可以实现更加复杂的线程同步操作。ReentrantLock 是 Lock 接口的一个实现类,它提供了与 synchronized 相同的互斥性和内存可见性,同时还提供了更多的高级功能,如可重入锁、公平锁等。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
private Lock lock = new ReentrantLock();
public void print() {
lock.lock();
try {
// 线程安全的代码
System.out.println(Thread.currentThread().getName() + " is running");
} finally {
lock.unlock();
}
}
}
2. Condition
Condition 接口是 Lock 接口提供的一个高级功能,它可以用于实现线程之间的通信。一个 Condition 对象可以和一个 Lock 对象关联,当一个线程调用 Condition 的 await() 方法时,它会释放掉与这个 Condition 对象关联的 Lock 对象的锁,并进入等待状态;当另一个线程调用 Condition 的 signal() 或 signalAll() 方法时,它会通知一个或所有正在等待的线程恢复执行。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private int count = 0;
public void produce() {
lock.lock();
try {
while (count >= 10) {
condition.await();
}
count++;
System.out.println(Thread.currentThread().getName() + " produce, count = " + count);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void consume() {
lock.lock();
try {
while (count <= 0) {
condition.await();
}
count--;
System.out.println(Thread.currentThread().getName() + " consume, count = " + count);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
3. Semaphore
Semaphore 是一种计数信号量,它可以用于控制同时访问某个资源的线程数量。Semaphore 维护了一个计数器,每当一个线程访问该资源时,计数器就会减一;当计数器为零时,所有试图访问该资源的线程都会被阻塞,直到计数器大于零。
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
private Semaphore semaphore = new Semaphore(5);
public void access() {
try {
semaphore.acquire();
// 线程安全的代码
System.out.println(Thread.currentThread().getName() + " is running");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
4. CountDownLatch
CountDownLatch 是一种同步工具类,它可以让一个或多个线程等待其他线程完成操作后再继续执行。CountDownLatch 维护了一个计数器,线程调用它的 await() 方法会阻塞,直到计数器变为零;而其他线程完成操作后,可以调用 CountDownLatch 的 countDown() 方法将计数器减一。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
private CountDownLatch countDownLatch = new CountDownLatch(3);
public void task() {
try {
// 模拟耗时操作
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + " is running");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
public void start() {
for (int i = 0; i < 3; i++) {
new Thread(this::task).start();
}
try {
countDownLatch.await();
System.out.println("All tasks have been completed");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5. CyclicBarrier
CyclicBarrier 是一种同步工具类,它可以让一组线程在达到某个共同点之前相互等待。CyclicBarrier 维护了一个计数器和一个栅栏点,每当一个线程到达栅栏点时,计数器就会减一;当计数器为零时,所有线程就可以继续执行。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
private CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
System.out.println("All threads have arrived at the barrier");
});
public void task() {
try {
// 模拟耗时操作
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + " has arrived at the barrier");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + " is running");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
public void start() {
for (int i = 0; i < 3; i++) {
new Thread(this::task).start();
}
}
}
JUC 中的常用工具类
1. Executors
Executors 是一个工具类,用于创建线程池。它提供了一些静态方法,可以方便地创建不同类型的线程池。常用的方法有:
- newFixedThreadPool(int nThreads):创建一个固定大小的线程池,该线程池中的线程数始终为 nThreads。
- newCachedThreadPool():创建一个缓存线程池,该线程池可以根据需要创建新的线程,但在有可用线程时重用旧线程。
- newSingleThreadExecutor():创建一个单线程池,该线程池中始终只有一个线程在工作。
- newScheduledThreadPool(int corePoolSize):创建一个定时任务线程池,该线程池可以在指定的延迟时间后执行任务,也可以按照固定的时间间隔执行任务。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorsDemo {
private ExecutorService executorService = Executors.newFixedThreadPool(5);
public void execute() {
executorService.execute(() -> {
// 线程安全的代码
System.out.println(Thread.currentThread().getName() + " is running");
});
}
public void shutdown() {
executorService.shutdown();
}
}
2. Future 和 FutureTask
Future 接口表示一个异步计算的结果,它提供了一些方法,可以查询计算是否完成、等待计算完成并获取结果。FutureTask 是 Future 接口的一个实现类,它可以用于异步执行任务,并且可以在任务执行完成后获取任务的执行结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTaskDemo {
private FutureTask<Integer> futureTask = new FutureTask<>(() -> {
// 模拟耗时操作
Thread.sleep((long) (Math.random() * 1000));
return 1;
});
public void start() {
new Thread(futureTask).start();
}
public void get() {
try {
int result = futureTask.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
3. ConcurrentHashMap
ConcurrentHashMap 是一个线程安全的哈希表,它可以在高并发的情况下提供更好的性能。它的实现原理是将整个哈希表分成多个段,每个段都是一个独立的哈希表,可以独立地进行添加、删除、修改等操作。这样,当多个线程同时访问不同的段时,就可以实现真正的并行操作,从而提高了并发性能。
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapDemo {
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
public void put(String key, int value) {
map.put(key, value);
}
public int get(String key) {
return map.get(key);
}
}
JUC 中的高级特性
1. AQS
AQS(AbstractQueuedSynchronizer)是 JUC 中的一个重要类,它提供了一种通用的同步框架,可以用于实现各种同步器,如锁、信号量、倒计时门栓等。AQS 的核心思想是基于一个先进先出(FIFO)的队列,来管理等待获取同步状态的线程。当一个线程获取同步状态失败时,它会被封装成一个节点,然后加入到等待队列中,等待其他线程释放同步状态后再次尝试获取。
2. ForkJoin
ForkJoin 框架是 JUC 中的一个高性能并行计算框架,它可以将一个大任务拆分成多个小任务,并行地执行这些小任务,最后将小任务的结果合并成大任务的结果。ForkJoin 框架的核心思想是“工作窃取”,即当一个线程的任务执行完毕后,它可以从其他线程的任务队列中窃取一个任务来执行,以此来实现负载均衡。
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo extends RecursiveTask<Integer> {
private int start;
private int end;
public ForkJoinDemo(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start <= 100) {
int sum = 0;
for (int i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
int mid = (start + end) / 2;
ForkJoinDemo left = new ForkJoinDemo(start, mid);
ForkJoinDemo right = new ForkJoinDemo(mid + 1, end);
left.fork();
right.fork();
return left.join() + right.join();
}
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinDemo task = new ForkJoinDemo(1, 1000);
Integer result = forkJoinPool.invoke(task);
System.out.println("Result: " + result);
}
}
总结
Java JUC 提供了一系列的工具类和接口,用于简化多线程编程。在实际开发中,我们应该根据具体的业务需求选择合适的工具类和接口,以提高程序的并发性能和可靠性。同时,我们还应该遵守多线程编程的最佳实践,如避免死锁、竞态条件等问题,以确保程序的正确性和稳定性。
公众号请关注"果酱桑", 一起学习,一起进步!