启动
start方法,会自动以信进程调用run方法
直接调用run方法,将变成串行执行
同一个线程,多次start会报错,只执行第一次start方法
多个线程启动,先后顺序是随机的
线程无需关闭,只要其run方法执行结束后,自动关闭
main函数(线程)可能早于新线程结束,整个程序并不终止
整个程序终止是等所有的线程都终止(包括main函数线程)
Thread vs Runnable
Thread占据了父类的名额,不如Runnable方便
Thread类实现 Runnable
Runnable启动时需要Thread类的支持
Runnable更容易实现多线程中资源共享
结论:建议实现Runnable接口来完成多线程
关键步骤加锁限制
互斥:某一个线程运行一个代码段(关键区),其他线程不能同时运行这个代码
同步:多个线程的运行,必须按照某一种规定的先后顺序来运行
互斥是同步的一种特例
互斥的关键字是synchronized
synchronized代码块/函数,只能一个线程进入
synchronized加大性能负担,但是使用简便
线程组ThreadGroup
线程的集合
树形结构,大线程组可以包括小线程组
可以通过enumerate方法遍历组内的线程,执行操作
能够有效管理多个线程,但是管理效率低
任务分配和执行过程高度耦合
重复创建线程,关闭线程操作,无法重用线程。
从JDK5开始提供Executor FrameWork
分离任务的创建和执行者的创建
线程重复利用(new线程的代价很大)
理解共享线程池的概念
预设好的多个Thread,可弹性增加
多次执行很多很小的任务
任务创建和执行过程解耦合
程序员无需关心线程池执行任务过程
主要类:
ExecutorService,ThreadPoolExecutor,Future
-Executor.newCachedThreadPool/ new FixedThreadPool来创建线程池
-ExecutorService 线程池服务
-Callable 具体的逻辑对象(线程类)
-Future 返回结果
主要类:
-ForkJoinPool 任务池
-RecursiveAction
-RecuresiveTask
适合分配量化不了的任务
常用的数据结构是线程不安全的
-ArratList,HashMap,HashSet非同步的
-多个线程同时读写,可能会抛出异常或者数据错误
传统的Vector,Hastable等同步集合性能过差
并发数据结构:数据添加和删除
-阻塞式集合:当集合为空或者满时,等待
-非阻塞式集合:当集合为空或者满时,不等待,返回null或异常
List
-Vector 同步安全,写多读少
-ArrayList 不安全
-Collection.synchronizedList 基于 synchronized,效率差
-CopyOnWriteArrayList 读多写少,基于复制机制,非阻塞
Set
-HashSet 不安全
-Collection.synchronizedSet 基于synchronized,效率差
-CopyOnWriteArraySet 基于CopyOnWriteArrayList实现 ,读多写少,非阻塞
Map
-HashMap 同步安全,写多读少
-HashMap 不安全
-Collection.synchronizedMap ,效率差
-ConcurrentHashMap 读多写少,非阻塞
Queue Deque
-ConcurrentLinkedQueue 非阻塞
-ArrayBlockingQueue/LinkedBlockingQueue 阻塞
Lock也可以实现同步的效果
-实现更复杂的临界区结构
-tryLock方法可以预判锁是否空闲
-允许分离读写的操作,多个读,一个写
-性能更好
ReentrantLock类,可重入的互斥锁
ReentrantReadWriteLock类,可重入的读写锁
lock和unlock函数
Semaphore
信号量:本质上是一个计数器
计数器大于0 可以使用,等于0不能使用
可以设置多个并发量,例如限制10个访问
Semaphore
-acquire 获取
-release 释放
比lock更近一步,可以控制多个同时访问关键区