java并发高级面试题(一)
提到多线程,当然要熟悉java提供的各种多线程相关的并发包了,而java.util.concurrent就是最最经常会使用到的,那么关于concurrent的面试题目有哪些呢?一起来看看吧。
问题1:什么是ConcurrentHashMap?它与HashMap的区别是什么?
回答: ConcurrentHashMap是java.util.concurrent包中的一个线程安全的哈希表实现。与普通的HashMap相比,ConcurrentHashMap在多线程环境下提供更好的性能和线程安全保障。
区别:
-
ConcurrentHashMap支持并发读写操作,而HashMap在多线程环境下需要额外的同步措施。
-
ConcurrentHashMap的put、remove等操作使用分段锁,只锁定部分数据,从而提高并发度。
-
ConcurrentHashMap允许多个线程同时进行读操作,而HashMap在读写冲突时需要互斥。
示例:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put(1, "One");
concurrentMap.put(2, "Two");
concurrentMap.put(3, "Three");
String value = concurrentMap.get(2);
System.out.println("Value at key 2: " + value);
}
}
问题2:什么是CopyOnWriteArrayList?它适用于什么样的场景?
回答: CopyOnWriteArrayList是java.util.concurrent包中的一个线程安全的动态数组实现。它适用于读多写少的场景,即在读操作远远多于写操作的情况下,使用CopyOnWriteArrayList可以避免读写冲突。
CopyOnWriteArrayList在写操作时会创建一个新的数组,复制旧数组中的数据,并添加新的元素,然后将新数组替换旧数组。因此,写操作不会影响读操作,读操作也不会影响写操作。
示例:
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("One");
list.add("Two");
list.add("Three");
for (String item : list) {
System.out.println(item);
}
}
}
问题3:什么是BlockingQueue?它的作用是什么?举例说明一个使用场景。
回答: BlockingQueue是java.util.concurrent包中的一个接口,表示一个支持阻塞的队列。它的主要作用是实现线程间的数据传递和协作。
BlockingQueue可以用于解耦生产者和消费者,让生产者和消费者线程在不同的速度进行操作。当队列为空时,消费者线程会阻塞等待,直到队列中有数据;当队列满时,生产者线程会阻塞等待,直到队列有空间。
示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
Thread producer = new Thread(() -> {
try {
queue.put("Item 1");
queue.put("Item 2");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
String item1 = queue.take();
String item2 = queue.take();
System.out.println("Consumed: " + item1 + ", " + item2);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
问题4:什么是Semaphore?它如何控制并发访问?
回答: Semaphore是java.util.concurrent包中的一个计数信号量。它可以用来控制同时访问某个资源的线程数量,从而实现对并发访问的控制。
Semaphore通过调用acquire()来获取一个许可证,表示可以访问资源,通过调用release()来释放一个许可证,表示释放资源。Semaphore的内部计数器可以控制同时获取许可证的线程数量。
示例:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); // 允许两个线程同时访问
Thread thread1 = new Thread(() -> {
try {
semaphore.acquire();
System.out.println("Thread 1 acquired a permit.");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println("Thread 1 released a permit.");
}
});
Thread thread2 = new Thread(() -> {
try {
semaphore.acquire();
System.out.println("Thread 2 acquired a permit.");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println("Thread 2 released a permit.");
}
});
thread1.start();
thread2.start();
}
}
问题5:什么是CountDownLatch?它适用于什么场景?
回答: CountDownLatch是java.util.concurrent包中的一个计数器,用于控制线程等待其他线程完成一组操作。它适用于一个线程需要等待其他多个线程完成某个任务后再继续执行的场景。
CountDownLatch的内部计数器可以初始化为一个正整数,每个线程完成一个操作后,调用countDown()方法来减少计数器的
值。当计数器减为0时,等待的线程将被释放。
示例:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(3); // 需要等待3个线程完成
Thread worker1 = new Thread(() -> {
System.out.println("Worker 1 is working...");
latch.countDown();
});
Thread worker2 = new Thread(() -> {
System.out.println("Worker 2 is working...");
latch.countDown();
});
Thread worker3 = new Thread(() -> {
System.out.println("Worker 3 is working...");
latch.countDown();
});
worker1.start();
worker2.start();
worker3.start();
try {
latch.await(); // 等待所有工作线程完成
System.out.println("All workers have completed.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}