Java并发编程 - 多线程/并发面试题集合(持续更新)

Posted helios-fz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java并发编程 - 多线程/并发面试题集合(持续更新)相关的知识,希望对你有一定的参考价值。

1. 现在有线程T1、T2和T3。你如何确保T2线程在T1之后执行,并且T3线程在T2之后执行。

https://www.cnblogs.com/helios-fz/p/11216925.html

 

2. Java 中新的Lock接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。

Lock接口的最大优势是它为读和写提供两个单独的锁(ReentrantReadWriteLock),ReentrantReadWriteLock的特点是:“读读共享”,“读写互斥”,“写写互斥”。(附:Lock取款机示例

高性能缓存简易示例:

public class ReadWriteMap {

    private final Map<Object, Object> map;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public ReadWriteMap(Map<Object, Object> map) {
        this.map = map;
    }

    public Object put(Object key, Object value) {
        try {
            writeLock.lock();
            return map.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }

    public Object get(Object key) {
        try {
            readLock.lock();
            return map.get(key);
        } finally {
            writeLock.unlock();
        }
    }
}

 

3.Java中wait和sleep方法有什么区别。

sleep() 是Thread类的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁(或监视器)依然保持。休眠时间结束后线程自动回到就绪状态。

wait() 是Object类的方法,调用此方法会让当前线程暂停执行指定的时间,并放弃对象锁进入等待池(wait pool)。只有调用对象的notify()或notifyAll()方法才能唤醒等待池中的线程进入等锁池(lockpool)。如果线程重新获得对象的锁就可以进入就绪状态。

wait()方法多用于线程间通信,而sleep()只是在执行时暂停。

 

4.如何在Java中实现一个阻塞队列。

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。
1)阻塞的插入:当队列满时,队列会阻塞插入元素的线程,直到队列不满。
2)阻塞的移除:当队列空时,队列会阻塞移除元素的线程,直到队列不空。

public class BlockingQueue {

    private final List<Object> queue = new LinkedList<>();
    private int capacity = 10;

    public BlockingQueue() {
    }

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized Object put(Object item) throws InterruptedException {
        while (queue.size() >= capacity) {
            wait();
        }
        queue.add(item);
        notifyAll();
        return item;
    }

    public synchronized void remove() throws InterruptedException {
        while (0 == queue.size()) {
            wait();
        }
        queue.remove(0);
        notifyAll();
    }

    public synchronized int getSize() {
        return queue.size();
    }
}

附:我在实现阻塞队列时遇到的一个问题 https://www.cnblogs.com/helios-fz/p/11721583.html

 

5.写一段死锁代码。说说你在Java中如何解决死锁。

https://www.cnblogs.com/helios-fz/p/11663518.html

 

6.Java中volatile关键字是什么。你如何使用它。它和Java中的同步方法有什么区别。

https://www.cnblogs.com/helios-fz/p/10935129.html

 

7.既然start()方法会调用run()方法,为什么我们调用 start() 方法,而不直接调用 run() 方法。

调用start()方法时,它会新建一个线程然后执行run()方法中的代码。

直接调用run()方法,则不会创建新线程,方法中的代码会在当前调用者的线程中执行。验证如下:

    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> System.out.println(Thread.currentThread().getName()));
        Thread thread2 = new Thread(() -> System.out.println(Thread.currentThread().getName()));

        thread1.setName("thread1");
        thread1.start();

        Thread.sleep(1000);

        thread2.setName("thread2");
        thread2.run();
    }

技术图片

 

8.什么是线程调度,Java中使用了什么线程调度方法。

线程调度是指按照特定机制为多个线程分配CPU的使用权。

一般的调度模型有两种:分时调度模型和抢占式调度模型。

  • 分时调度模型是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片。
  • 抢占式调度模型是指优先让可运行池中优先级高的线程占用CPU。如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。

Java使用的是抢占式调度。

以上是关于Java并发编程 - 多线程/并发面试题集合(持续更新)的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程面试题(面试必备)

Java多线程并发基础面试题回答

JUC并发编程线程池及相关面试题 详解

JAVA多线程和并发基础面试题

还搞不定Java多线程和并发编程面试题?你可能需要这一份书单!

还搞不定Java多线程和并发编程面试题?你可能需要这一份书单!