java多线程开发手册

### 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结构。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容