Java多线程面试之线程通信

1.为什么wait/notify/notifyAll是Object类中的方法,而不是Thread里的方法

线程为了进入临界区(也就是同步块内),需要获得锁并等待锁可用,它们并不知道也不需要知道哪些线程持有锁,它们只需要知道当前资源是否被占用,是否可以获得锁,所以锁的持有状态应该由同步监视器来获取,而不是线程本身。

2.等待通知机制中wait/notify/notifyAll方法的作用

1.使用wait(),notify(),notifyAll()方法之前,要获取同一个对象的锁。
2.调用wait()方法之后,线程会从RUNABLE状态变为WAITING状态,并会释放对象锁,并会将线程移入到对象的等待队列中。
3.notify()和notifyAll()调用之后,等待的线程的wait方法并不会立马返回,需要锁空闲的时候,等待的线程获取了锁,wait()方法才会返回。
4.调用了notify()和notifyAll()方法之后,notify会将一个线程从等待队列放置到同步队列,同步队列是因为锁被占有而处于BLOCKED阶段的线程,notifyAll则是将等待队列中所有的线程都移动到同步队列,这两个方法都是将线程从WAITING状态变为BLOCKED状态。

3.notify/notifyAll的区别?

notify只要唤醒一个线程就会有等待池进入该对象的锁池,会导致死锁。
notifyALL需要唤醒所有线程才能有等待池进入锁池,不会导致死锁。

4.生产者消费者模型
class BlockingQueue<E>{
    private final LinkedList<E> queue = new LinkedList<>();
    private static int max; //表示阻塞队列存储元素的最大个数
    private static final int DEFAULT_MAX_VALUE = 10;

    public BlockingQueue(){
        this(DEFAULT_MAX_VALUE);
    }

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

    //生产数据
    public void put(E value){
        synchronized (queue){
            //判断当前队列是否有位置存放新生产的数据
            if(queue.size() >= max){
                System.out.println(Thread.currentThread().getName() +" :: queue is full");
                try {
                    //没有位置,当前生产数据的线程需要阻塞
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+":: the new value " +value+" has been produced");
            queue.addLast(value);
            queue.notifyAll();//期望唤醒消费者线程
        }
    }

    //消费数据
    public E take(){
        synchronized (queue){
            //判断当前队列是否存在可消费的数据
            if(queue.isEmpty()){
                System.out.println(Thread.currentThread().getName() +" :: queue is empty");
                try {
                    //不存在,则调用消费数据的线程阻塞
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            E result = queue.removeFirst();
            queue.notifyAll();//期望唤醒生产者线程

            System.out.println(Thread.currentThread().getName()+":: the value " +result + " has been consumed");
            return result;
        }
    }
}

public class ProducerAndConsumerDemo {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new BlockingQueue<Integer>();
        new Thread("Producer"){
            @Override
            public void run() {
                while(true){
                    queue.put((int)(Math.random() * 1000));
                }
            }
        }.start();

        new Thread("Consumer"){
            @Override
            public void run() {
                while(true){
                    queue.take();
                    try {
                        TimeUnit.MILLISECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
5.await方法的使用为什么需要绑定到一个condition对象上

Condition接口的方法

 boolean await(long time, TimeUnit unit) throws InterruptedException;

    // 当前线程进入等待状态直到被通知、中断或者到某个时间。如果未超时返回true,否则返回false 

在这里插入图片描述

6.一个ReenteantLock对象可以拥有多个Condition对象,这些对象有什么作用

ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。每一个Lock可以有任意数据的Condition对象,Condition是与Lock绑定的,所以就有Lock的公平性特性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值