知识总结
通信线程
- 等待
public final void wait()
public final void wait(long timeout)
必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在o的等待队列中。释放锁,进入等待队列。 - 通知
public final void notify()
public final void notifyAll()
必须在obj加锁的同步代码块中。从obj的Waiting中释放一个或全部线程。对自身没有任何影响。
高级多线程
- 线程池
线程容器,可设定线程分配的数量上限
将预先创建的线程对象存入池中,并重用线程池中的线程对象
避免频繁的创建和销毁 - 获取线程池
常用的线程池接口和类(所在包java.util.concurrent)
Executor : 线程池的顶级接口
ExecutorService : 线程池接口,可通过submit(Runnable task)提交任务代码
Executors工厂类 : 通过此类获得一个线程池
通过newFixedThreadPool(int nThreads) 获取固定数量的线程池。参数:指定线程池中的线程的数量
通过newCachedThreadPool()获取动态数量的线程池,如果不够则创建新的,没有上限
Callable接口
Public interface Callable{
public V call() throws Execepion;
}
JDK5加入,与Runnable接口类似,实现之后代表一个线程任务
Callable具有泛型返回值、可以声明异常
Future接口
概念:异步接受ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值
方法:V get()以阻塞形式等待Future中的异步处理结果(call()的返回值)
Lock接口
常用方法
void lock() //获取锁,如果锁被占用,则等待
voolean tryLock() //尝试获取锁(成功返回true。失败返回false,不阻塞)
void unlock //锁释放
重入锁:ReentrantLock ,Lock接口的实现类
读写锁:ReentrantReadWriteLock , 一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁;支持多次分配读锁,使多个读操作可以并发执行
线程安全的类
- CopyOnWriteArrayList
线程安全的ArrayList , 加强版读写分离
写有锁,读无锁,读写之间不阻塞,优于读写锁
写入时,先copy一个容器副本、再添加新元素,最后替换引用
使用方式与ArrayList无异 - CopyOnWriteArraySet
线程安全的Set , 底层使用CopyOnWriteArrayList实现
唯一不同于,使用addIfbsent()添加元素,会遍历数组
如存在元素,则不添加 - ConcurrenthashMap
初始容量默认为16段(Segment),使用分段锁设计
不对整个Map加锁,而是为每个Segment加锁
当多个对象存入同一个Segment时,才需要互斥
最理想状态为16个对象分别存入16个Segment , 并行数量16
使用方式与HashMap无异
Queue接口
Collection的子接口,表示队列FIFO
- 常用方法:
返回特殊值:
boolean offer(E e) //顺序添加一个元素(到达上限后,再添加则会返回false)
E poll( ) //获取第一个元素并移除(如果队列没有元素时,则返回null)
E keep( ) //获得第一个元素但不移除(如果队列没有元素时,则返回null) - ConcurrentLinkedQueue
线程安全、可高效读写的队列,高并发下性能最好的队列
无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
V:要更新的变量、E:预期值、N:新值
只由当V==E时,V=N;否则表示已被更新过,则取消当前操作 - BlockingQueue接口(阻塞队列)
Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法
方法:
void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待
E take() //获取并移除此队列头部元素,如果没有可用元素,则等待
阻塞队列:
ArrayBlockingQueue:数据结构实现,有界队列
LinkedBlockingQueue:链表结构实现,无界队列