JavaSE基础入门_015_高级多线程
线程池概念
-
问题:
-
线程是宝贵的内存资源、单个线程约占 1MB 空间,过多分配易造成内存溢出。
-
-
-
线程池:
-
线程容器,可设定线程分配的数量上限。
-
将预先创建的线程对象存入池中,并重用线程池中的线程对象。
-
避免频繁的创建和销毁。
-
线程池原理
-
将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程。
创建线程池
-
常用的线程池接口和类(所在包:java.util.concurrent):
-
Executor:线程池的顶级接口。
-
ExecutorService:继承了Executor接口,线程池接口,可通过 submit( Runnable task ) 提交任务代码。
-
Executors工厂类【工具类】:通过此类可以获得一个线程池。
-
通过 newFixedThreadPool ( int nThreads ) 获取固定数量的线程池. 参数: 指定线程中线程的数量.
-
通过 newCatchedThreadPool ( ) 获取动态数量的线程池, 如不够则创建新的, 没有上限
-
Callable接口
public interface Callable<V>{
public V call() throws Exception;
}
-
JDK 5 加入, 与 Runnable 接口类似, 实现之后代表一个线程任务。
-
Callable 具有泛型返回值、可以声明异常。
Future接口
-
Future:表示将要完成任务的结果。
-
需求:使用两个线程,并发计算 1~50、50~100的和,在进行汇总计算
-
表示 ExecutorService.submit( ) 所返回的状态结果, 就是 call( ) 的返回值
-
方法: V get() 以阻塞形式等待 Future 中的异步处理结果 ( call() 的返回值 )
线程的同步
-
同步:
-
形容一次方法的调用, 同步一旦开始, 调用者必须等待该方法返回, 才能继续.
-
注: 单条执行路径
-
线程的异步
-
异步:
-
形容一次方法调用, 异步一旦开始, 像是一次消息传递, 调用者告知之后立刻返回. 二者竞争时间片, 并发执行.
-
注: 多条执行路径
-
Lock接口
-
JDK5 加入, 与 synchronized 比较, 显示定义, 结构更灵活.
-
提供更多实用性方法, 功能更强大、性能更优越。
-
常用方法:
-
void lock() //获取锁,如锁被占用,则等待
boolean tryLock() //尝试获取锁(成功返回true。失败返回false,不阻塞)
void unlock() //释放锁
-
-
利用其自身的实现类来实现接口调用、创建锁对象。
重入锁
-
ReentrantLock:Lock 接口的实现类,与 synchronized 一样具有互斥锁功能。
读写锁
-
ReentrantReadWriteLock:
-
一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
-
支持多次分配读锁,使多个读操作可以并发执行。
-
读锁不互斥、写锁才互斥。
-
-
互斥规则:
-
写—写:互斥,阻塞
-
读—写:互斥,读阻塞写、写阻塞读
-
读—读:不互斥、不阻塞
-
在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。
-
线程安全的集合
-
Collection体系集合中, 除Vector以外的线程安全集合.
Collections中的工具方法
-
Collections工具类提供了多个可以获得线程安全集合的方法.
-
public static <T> Collection <T> synchronizedCollection(Collection<T> c)
public static <T> List <T> synchronizedList(List<T> list)
public static <T> Set <T> synchronizedSet(Set<T> s)
public static <T> Map <K,V> synchronizedMap(Map<K,V> m)
public static <T> SortedSet <T> synchronizedSortedSet(SortedSet<T> s)
public static <T> SortedMap <K,V> synchronizedSortedMap(SortedMap<K,V> m)
-
-
JDK 1.2 提供, 接口统一、维护性高,但性能没有提升,均以 synchronized 实现。
CopyOnWriteArrayList
-
线程安全的 ArrayList,加强版的读写分离。
-
写有锁,读无锁,读写之间不阻塞,优于读写所。
-
写入时,先 copy 一个容器副本、再添加新元素,最后替换引用。
-
使用方式和 ArrayList 无异。
CopyOnWriteArraySet
-
线程安全的Set,底层使用 CopyOnWriteArrayList 实现。
-
唯一不同在于,使用 addIfAbsent() 添加元素,会遍历数组,
-
如存在元素,则不添加(扔掉副本)。
-
注意: 它是有序的
Queue接口(队列)
-
Collection的子接口, 表示队列 FIFO 先进先出.
-
常用方法:
-
抛出异常:
-
boolean add(E e) // 顺序添加一个元素 (到达上限后, 再添加则会抛出异常)
E remove() // 获得第一个元素并移除 (如果队列没有元素时, 则抛出异常)
E element() // 获得第一个元素但不移除 (如果队列没有元素时, 则抛出异常)
-
-
返回特殊值: 推荐使用
-
boolean offer(E e) // 顺序添加一个元素 (到达上限后, 再添加则会返回false)
E poll() // 获得第一个元素并移除 (如果队列没有元素时, 则返回null)
E peek() // 获得第一个元素但不移除 (如果队列没有元素时, 则返回null)
-
-
ConcurrentLinkedQueue
-
线程安全、可高效读写的队列,高并发下性能最好的队列。
-
无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
-
V:要更新的变量、E:预期值、N:新值
-
只有当 V==E时,V=N;否则表示已被更新过,则取消当前操作。
BlockingQueue接口 (阻塞队列)
-
Queue的子接口,阻塞的队列,增加了两个线程状态为无期限等待的方法。
-
方法:
-
void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待。
E take() //获取并移除此队列头部元素,如果没有可用元素,则等待
-
-
可用于解决生产者、消费者问题
-
当队列数量达到上限时,自动阻塞当前进程。
阻塞队列
-
ArrayBlockingQueue:
-
数组结构实现,有界队列。(手工固定上限)
-
-
LinkedBlockingQueue:
-
链表结构实现,有界队列。(默认上限 Integer. MAX_VALUE)
-
ConcurrentHashMap
-
初始容量默认为16段(Segment),使用分段锁设计。
-
不对整个Map加锁,而是为每个 Segment 加锁。
-
当多个对象存入同一个Segment时,才需要互斥。
-
最理想状态为16个对象分别存入16个Segment,并行数量16。
-
使用方式与 HashMap 无异。
总结
-
ExecutorService 线程池接口、Executors 工厂。
-
Callable 线程任务、Future 异步返回值。
-
Lock、ReentrantLock 重入锁、ReentrantReadWriteLock 读写锁.
-
CopyOnWriteArrayList 线程安全的 ArrayList。
-
CopyOnWriteArraySet 线程安全的 Set。
-
ConcurrentLinkedQueue 线程安全的 Queue。
-
ArrayBlockingQueue 线程安全的阻塞 Queue。(生产者、消费者)
-