### Java多线程
进程和线程的概念
进程和线程的区别
Java语言内置多线程支持
多线程编程的特点:
* 多线程需要读写共享数据
* 多线程经常需要同步
* 多线程编程的复杂度高,调试更困难
### 创建新线程
1. 从Thread派生
2. 实现Runnable接口
必须调用`start()`方法才能启动新线程
`Thread.sleep()`可以把当前线程暂停一段时间
### 线程的状态
一个线程对象只能调用一次start()
Java线程对象Thread的状态包括:
New / Runnable / Blocked / Waiting / Timed Waiting / Terminated
通过对另一个线程对象调用join()方法可以等待其执行结束
可以指定等待时间,超过等待时间线程仍然没有结束就不再等待
对已经运行结束的线程调用join()方法会立刻返回
### 中断线程
调用interrupt()方法可以中断一个线程
通过检测isInterrupted()标志获取当前线程是否已中断
如果线程处于等待状态,该线程会捕获InterruptedException
isInterrupted()为true或者捕获了InterruptedException都应该立刻结束
通过标志位判断需要正确使用volatile关键字
volatile关键字解决了共享变量在线程间的可见性问题
### 守护线程
守护线程是为其他线程服务的线程
所有非守护线程都执行完毕后,虚拟机退出
守护线程不能持有资源(如打开文件等)
创建守护线程:setDaemon(true)
### 线程同步
多线程同时修改变量,会造成逻辑错误
需要通过synchronized同步
同步的本质就是给指定对象加锁
注意加锁对象必须是同一个实例
对JVM定义的单个原子操作不需要同步
### synchronized方法
用synchronized修饰方法可以把整个方法变为同步代码块
synchronized方法加锁对象是this
通过合理的设计和数据封装可以让一个类变为“线程安全”
一个类没有特殊说明,默认不是thread-safe
多线程能否访问某个非线程安全的实例,需要具体问题具体分析
### 死锁
死锁产生的条件:
多线程各自持有不同的锁,并互相试图获取对方已持有的锁,双方无限等待下去:导致死锁
如何避免死锁:
多线程获取锁的顺序要一致
### wait / notify
wait / notify用于多线程协调运行:
在synchronized内部可以调用wait()使线程进入等待状态
必须在已获得的锁对象上调用wait()方法
在synchronized内部可以调用notify / notifyAll()唤醒其他等待线程
必须在已获得的锁对象上调用notify / notifyAll()方法
### ReentrantLock
ReentrantLock可以替代synchronized
ReentrantLock获取锁更安全
必须使用`try … finally`保证正确获取和释放锁
`tryLock()`可指定超时
### ReadWriteLock
使用ReadWriteLock可以提高读取效率:
* ReadWriteLock只允许一个线程写入
* ReadWriteLock允许多个线程同时读取
* ReadWriteLock适合读多写少的场景
### Condition
Condition可以替代wait / notify
Condition对象必须从ReentrantLock对象获取
ReentrantLock+Condition可以替代synchronized + wait / notify
### concurrent集合
使用java.util.concurrent提供的Blocking集合可以简化多线程编程:
* CopyOnWriteArrayList
* ConcurrentHashMap
* CopyOnWriteArraySet
* ArrayBlockingQueue
* LinkedBlockingQueue
* LinkedBlockingDeque
多线程同时访问Blocking集合是安全的
尽量使用JDK提供的concurrent集合,避免自己编写同步代码
### Atomic
使用java.util.atomic提供的原子操作可以简化多线程编程:
AtomicInteger/AtomicLong/AtomicIntegerArray等
原子操作实现了无锁的线程安全
适用于计数器,累加器等
### ExecutorService
JDK提供了ExecutorService实现了线程池功能
线程池内部维护一组线程,可以高效执行大量小任务
Executors提供了静态方法创建不同类型的ExecutorService
常用ExecutorService:
* FixedThreadPool:线程数固定
* CachedThreadPool:线程数根据任务动态调整
* SingleThreadExecutor:仅单线程执行
必须调用shutdown()关闭ExecutorService
ScheduledThreadPool可以定期调度多个任务(可取代Timer)
### Future
Future表示一个未来可能会返回的结果
提交Callable任务,可以获得一个Future对象
可以用Future在将来某个时刻获取结果
### CompletableFuture
CompletableFuture的优点:
* 异步任务结束时,会自动回调某个对象的方法
* 异步任务出错时,会自动回调某个对象的方法
* 主线程设置好回调后,不再关心异步任务的执行
CompletableFuture对象可以指定异步处理流程:
* thenAccept()处理正常结果
* exceptional()处理异常结果
* thenApplyAsync() 用于串行化另一个CompletableFuture
* anyOf / allOf 用于并行化两个CompletableFuture
### Fork/Join
Fork/Join是一种基于“分治”的算法:分解任务+合并结果
ForkJoinPool线程池可以把一个大任务分拆成小任务并行执行
任务类必须继承自RecursiveTask/RecursiveAction
使用Fork/Join模式可以进行并行计算提高效率
调用Thread.currentThread()获取当前线程。
JDK提供了ThreadLocal,在一个线程中传递同一个对象。
ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的。
ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递)
使用ThreadLocal要用try … finally结构。