Java多线程快问快答,十分钟助力拿下offer(二)

179 阅读5分钟

一、在Java中如何唤醒一个阻塞的线程

首先,如果是IO阻塞,普通方法是无法终止线程。

第二,如果线程是因为wait,sleep等方法进入的阻塞,可以使用中断线程,并且抛出InterruptedException异常来唤醒它。

对阻塞方法的大致分类:

(1)会抛出InterruptedException异常的方法:wait,sleep,join,Lock.lockInterruptibly等,针对这类方法,我们在内部处理好异常(要么完全内部处理,要么把这个异常抛出去),然后就可以实现唤醒。

(2)不会抛InterruptedException异常的方法:Scoket的I/O,同步I/O,Lock.lock等。对于I/O类型,我们可以关闭底层通道。比如Scoket的I/O,关闭底层套接字,然后抛出异常处理就好了;同步的I/O,关闭底层Channel的通道并处理异常;对于Lock.lock方法,我们可以改成使用Lock.lockInterruptibly方法去实现。

二、多线程中submit方法和execute方法的区别

1)submit(Callable task)、submit(Runnable task, T result)、submit(Runnable task)归属于ExecutorService接口。

execute(Runnable command)归属于Executor接口。ExecutorService继承了Executor。

2)submit()方法,可以提供Future 类型的返回值;

execute()方法,无返回值。

3)sumbit()方法入参可以为Callable,也可以为Runnable;

execute()方法入参为Runnable。

三、Java并发包下常用的类有哪些

这里简单列举几个常用的类,以及作用,并不深入展开描述。

比如,Atomic,Lock,BlockingQueue,BlockingDeque,ConcurrnetMap,CountDownLatch,Cyclicbarrier,ExecutorService,CopyOnWriteList,ThreadLocal等。

1,Atomic

比如AtomicInteger,AtomicLong等类提供了多种方法,可以原子性的为参数赋值,取值,交换值(getAndSet),比较并且 设置值(CAS)(失败就重试,直到没有冲突为止)等。

2,Locks

ReentrantLock:可重入的互斥锁,即同一线程可以多次获取锁,线程之间是互斥的。使用CAS+CLH(双向链表)实现的。

ReentrantReadWriteLock:可重入的读写锁,是ReentrantLock的增强,是更细粒度的控制。在特殊场景会使用,分为readLock和writeLock,读读共享,读写和写写排他。

StrampLock:读写并发锁,适用于读远远大于写的场景。

3,BlockingQueue

阻塞队列提供了一个队列可以遵循FIFO的放入和取出的操作,如果队列满了放入就会阻塞,相反如果队列为空取出就会阻塞。BlockingQueue是一个接口类,有多种实现类,常用的有如下5个。

ArrayBlockingQueue:数组阻塞队列,是用数组实现的。是有界的,只能在初始化时指定队列容量大小。内部只有一个ReentrantLock,读和写使用一个锁,因此效率不高。

LinkedBlockingQueue:链表阻塞队列,使用链表实现的。它可以是有界的也可以是无界的,内部有两个ReentrantLock,读和写是分离的,因此性能要比ArrayBlockingQueue要高。但创建和销毁Node,高并发对GC有一定压力。

PriorityBlockingQueue:优先级阻塞队列,基于最小二叉堆实现,线程安全的无界队列。构造器传入初始值和比较器规则。根据比较器规则来对内部元素排序。

SynchronousQueue:同步阻塞队列。内部只能存放一个元素,如果满了就放入就会阻塞,相反如果为空取出就阻塞。

DelayQueue:延迟阻塞队列,无界队列。内部使用优先级阻塞队列实现,只有元素过期才能取出来,顺序按元素过期时间长短排序,队头是过期时间最长的元素,使用ReentrantLock实现线程安全。

4,BlockDeque

LinkedBlockingDeque:双端链式阻塞队列,默认是无界的,也可以指定容量。该阻塞队列同时支持

FIFO和FILO两种操作方式,队头和队尾都可以执行插入取出的操作。使用一把锁+两个条件维持队列的同步,

和ArrayBlockingQueue的原理一样。

5,ConcurrentMap

支持并发操作的Map。

ConcurrentHashMap:查看具体的内容讲解。

ConcurrentSkipListMap:

使用跳表skipList实现,可以支持排序,对应非线程安全的TreeMap是使用红黑树实现的。ConcurrentSkipListMap

适用于高并发的写操作(千万级),因为它锁住的节点少,相对于红黑树平衡造成的锁竞争,ConcurrentSkipListMap

效率更高。

6,CountDownLatch

具有计时器的功能,等待其他线程执行完毕,主线程再继续执行。

初始化指定倒计时的值,CountDownLatch latch = new CountDownLatch(3)并使用latch.await()等待执行,

当其他线程调用3此latch.countDown()就触发主线程继续。

CountDownLatch是一次性的,使用完就当前对象失效了。

7,CyclicBarrier

循环栅栏。允许定义N个线程全部执行到满足某一个条件后,再全部线程一起执行。

CyclicBarrier可以重复使用。

8,ExecutorService

线程池服务接口,有两种具体实现类。

ThreadPoolExecutor:线程池实现类。

核心参数有:

corePoolSize int 核心线程池大小

maximumPoolSize int 最大线程池大小

keepAliveTime long 线程最大空闲时间

unit TimeUnit 时间单位

workQueue BlockingQueue 线程等待队列

threadFactory ThreadFactory 线程创建工厂

handler RejectedExecutionHandler 拒绝策略

ForkJoinPool:

实现了Executor接口,支持将一个大任务分为若干个子任务交给子线程去处理,然后合并为一个结果集。

采用了分治和递归的思想。内部维护了多个队列。

9,CopyOnWriteList

CopyOnWriteList是并发场景下的List容器,适用于读远大于写的场景。相对于Vector的线程安全的List,Vector

所有方法上都有Synchronized同步锁,会造成大量的锁竞争。CopyOnWriteList使用读写分离的机制,它实现了无锁并发读,

写操作加锁,发生在新的副本上,写完后将原容器指向副本。

10,ThreadLocal

ThreadLocal用于处理同一线程数据共享的操作类。目的减少参数传递,和不同线程之间的数据隔离。

原理:内部使用静态的ThreadLocalMap对象存放元素,同一线程使用同一个ThreadLocalMap,key是ThreadLocal对象,value是存放的值。

ThreadLocalMap使用Entry数组实现,是一个弱引用,当线程被销毁时ThreadLocalMap也会被回收。