Java JUC面试题解析

发布时间:2023-05-23

一、JUC相关概念

Java JUC(j.u.c)表示Java并发编程工具包,是Java SE 5.0提供的线程处理API中最重要的一个,它主要位于java.util.concurrent包中。JUC中包含了许多工具类,可以让我们更加方便地进行多线程开发。下面我们来详细介绍几个与JUC相关的概念。

1.1 原子性

原子性指的是一个操作是不可分割的,也就是说在进行这个操作时,不存在其他的线程同时在进行同样的操作。Java中提供了一些原子性的操作类,常见的有AtomicInteger、AtomicBoolean等,它们可以保证操作的原子性,从而避免了线程安全问题。

public class AtomicIntegerTest {
    private static AtomicInteger count = new AtomicInteger();
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                for (int j = 0; j < 10; j++) {
                    System.out.println(count.incrementAndGet());
                }
            });
        }
        executor.shutdown();
    }
}

1.2 可见性

可见性指的是当一个线程对共享变量进行了修改后,其他线程能够立即看到这个修改。Java中通过volatile关键字来实现可见性。

public class VolatileVariableTest {
    private static volatile boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (!flag) {}
            System.out.println("Flag has been changed to true.");
        }).start();
        Thread.sleep(1000);
        flag = true;
        System.out.println("Flag has been set to true.");
    }
}

1.3 有序性

有序性指的是在并发情况下,程序的执行结果与理论预期的结果是一致的。Java中可以通过synchronized关键字或ReentrantLock类来实现有序性。

public class SynchronizedTest {
    private static int count = 0;
    public static synchronized void increase() {
        count++;
    }
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                for (int j = 0; j < 10; j++) {
                    increase();
                }
            });
        }
        executor.shutdown();
        while (!executor.isTerminated()) {}
        System.out.println(count);
    }
}

二、JUC的重要组件

Java JUC包含了很多重要的组件,这些组件可以让我们更加方便地进行多线程开发。下面我们来介绍JUC中比较重要的几个组件。

2.1 Semaphore

Semaphore是一个计数信号量,用于控制同时访问某个资源的线程数量。例如,有一个任务需要保证最多只能有5个线程同时执行,那么我们可以通过Semaphore来实现。

public class SemaphoreTest {
    private static Semaphore semaphore = new Semaphore(5);
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " is using the resource.");
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + " has released the resource.");
                }
            });
        }
        executor.shutdown();
    }
}

2.2 CountDownLatch

CountDownLatch是一个同步工具类,它可以让某个线程等待特定的一组操作完成后再继续执行。例如,我们可以让主线程等待10个子线程全部执行完毕后再继续执行。

public class CountDownLatchTest {
    private static CountDownLatch latch = new CountDownLatch(10);
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " is executing.");
                    Thread.sleep(500);
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        latch.await();
        System.out.println("All threads have finished executing.");
        executor.shutdown();
    }
}

2.3 CyclicBarrier

CyclicBarrier是另一个同步工具类,它可以让一组线程互相等待,直到到达某个屏障点后再一起继续执行。例如,我们可以让10个线程分别执行不同的任务,然后在某个节点处等待,等待所有线程都到达之后再继续执行。

public class CyclicBarrierTest {
    private static CyclicBarrier barrier = new CyclicBarrier(10);
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " is executing.");
                    Thread.sleep(500);
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + " continues executing.");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}

三、JUC中的线程安全容器

Java JUC包含了很多线程安全容器,它们可以让我们在多线程环境下更加安全地进行元素的添加、删除和读取。下面我们来介绍JUC中比较重要的几个线程安全容器。

3.1 ConcurrentHashMap

ConcurrentHashMap是一个支持高并发、线程安全的HashMap,它内部使用了分段锁机制,可以保证并发访问的安全性和效率。

public class ConcurrentHashMapTest {
    private static ConcurrentHashMap map = new ConcurrentHashMap<>();
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                for (int j = 0; j < 10; j++) {
                    map.put(UUID.randomUUID().toString(), j);
                }
            });
        }
        executor.shutdown();
        while (!executor.isTerminated()) {}
        System.out.println(map.size());
    }
}

  

3.2 CopyOnWriteArrayList

CopyOnWriteArrayList是一个支持高并发、线程安全的List,它内部使用了一种叫做“写时复制”的机制,写操作时会先复制出一个新的List进行操作,操作完成后再将原List指针指向新的List。因为读操作不需要加锁,所以它的读操作速度比较快,但写操作则比较慢。

public class CopyOnWriteArrayListTest {
    private static CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                for (int j = 0; j < 10; j++) {
                    list.add(UUID.randomUUID().toString());
                }
            });
        }
        executor.shutdown();
        while (!executor.isTerminated()) {}
        System.out.println(list.size());
    }
}

  

3.3 BlockingQueue

BlockingQueue是一个支持阻塞的队列,在多线程环境下可以让生产者线程和消费者线程更加安全地进行交互。BlockingQueue提供了一些常用的实现类,例如ArrayBlockingQueue、LinkedBlockingQueue等。

public class BlockingQueueTest {
    private static BlockingQueue queue = new LinkedBlockingQueue<>();
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    queue.put(UUID.randomUUID().toString());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        executor.execute(() -> {
            try {
                while (true) {
                    String element = queue.take();
                    System.out.println(element);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        executor.shutdown();
    }
}