java并发编程基础面试问答

1.什么是FutureTask?使用ExecutorService启动任务

在Java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成get方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行。

2.什么是并发容器的实现?

何为同步容器:可以简单理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。比如Voctor,Hashtable,以及Collectons.synchronizedSet,synchronizedList等方法返回的容器。可以通过查看Vector,hashtable等这些同步容器的上线代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized。

并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如在ConcurrentHashMap中采用了一种颗粒度更细的加锁机制,可以成为分段锁,在这种锁机制下,允许任意数量的读取线程并发访问map,并且执行读操作的线程和写操作的线程也可以并发的访问map,同时允许一定数量的写操作线程并发地修改map,所以它可以再并发环境下实现更高的吞吐量。

3.什么是Callable和Future?

Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。

可以任务是带有回调的Runnable。

Future接口表示异步任务,是还没有完成的任务给出的未来结果。所有说Callable用于产生结果,Future用于获取结果。

4.什么是阻塞队列?它的实现原理是什么?如何使用它实现生产者和消费者?

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。

这两个附加的操作是:在队列为空时,获取元素的线程会等待变为非空。当队列满时,存储元素的线程会等待队列可用。

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

JDK7提供了7个阻塞队列。分别是:

ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列;

LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列;

PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列;

DelayQueue:一个使用优先级队列实现的无界阻塞队列;

SyncronousQueue:一个不存储元素的阻塞队列;

LinkedTransferQueue:一个由链表结构组成的无界阻塞队列;

LinkedBlockingQueue:一个由链表结构组成的双向阻塞队列;

Java 5之前实现同步存取时,可以使用普通的一个集合,然后再使用线程的协作和线程同步可以实现生产者,消费者模式,主要的技术就是利用好wait、notify、notifyAll、syncronized这些关键字。而在Java5之后,可以使用阻塞队列来实现,此方式大大减少了代码量,使得多线程编程更加容易,安全方面也有保障。

BlockingQueue接口是Queue的子接口,它的主要用途并不是作为容器,而是作为线程同步的工具,因此它具有一个很明显的特征,当生产者线程试图向BlockingQueue放入元素时,如果队列已满,则线程阻塞,当消费者线程试图从中取出元素时,如果队列为空,则该线程会被阻塞,正是因为它具有这个特性,所有在程序中多个线程交替向BlockingQueue中放入元素,取出元素,它可以很好的控制线程之间的通信。

阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入对了,然后解析线程不断从队列去数据解析。

5.什么是竞争条件?怎么发现和解决竞争?

当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,则我们认为发生了竞争条件(race condition)。

6.为什么调用start()方法时会执行run方法,能否直接调用run()方法?

当调用start()方法时将创建新的线程,并且执行在run()方法里面的代码。但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码,只会把run方法当做普通方法去执行。

7.Java中CycliBarriar和CountdownLatch有什么区别

CyclicBarrier可以成功使用,而CountdownLatch 不能重复使用。

Java的concurrent包里面的CountDownLatch其实可以把它看做一个计数器,只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。

你可以向CountDownLatch对象设置一个初始的数字作为计数器,也就是同时只能有一个线程减去这个计数器里面的值。

可以向CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器值被其他的线程减去为0为止。

所以在当前计数达到零之前,await方法会一直受阻塞。之后,会释放所有等待的线程,await的所有后续调用都将立即返回。这种现象只能出现一次,计数无法被重置。如果需要重新计数,请考虑使用CyclicBarrier。

CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,当必须要等到其他任务执行完毕之后才可以继续往下执行。假如我们这个想继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,这道这个CountDownLatch对象的计数减到0为止。

CyclicBarrier一个同步辅助类,它允许一组线程相互等待,直到达到某个公共屏障点(common barrier point)。在涉及一组固定大小的线程的过程中,这些线程必须不时的相互等待,此时CyclicBarrier很有用。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier。

8.SynchronizedMap和ConcurrentHashMap有什么区别?

SyncronizedMap一次锁住整张表来保证线程安全,所以每次只能有一个线程来访问map。

ConcurrentHashMap使用分段锁来保证在多线程下的性能。ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

另外ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,interator完成后再将头指针替换为新的数据。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纵然间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值