第12章 并发
- 进程:每个进程拥有自己的一套完整变量;
- 线程:多个线程共享数据,通信更有效、更容易,开销小。
12.1 什么是线程
// Runnable接口,只有一个方法run(),函数式接口
Runnable r = () -> { task code }; // 用lambda创建一个实例
var t = new Thread(r);
t.start();
// 不要用Thread或Runnable对象的run方法,只是在同一个进程中执行;用Thread.start。创建新进程
12.2 线程状态
- new:新建
var t = new Thread(r); // 还未运行
; - Runnable:可运行
t.start(); // 可能正在运行也可能没运行,操作系统提供运行时间
Thread.yield(); // 当前正在执行的线程交出运行权 静态方法
; - Blocked:阻塞
// 试图获取内部对象锁
; - Waiting:等待
Thread.join(); // 线程等待另一个线程通知调度器出现一个条件时
; - Timed waiting:计时等待
Thread.sleep(); // 调用带超时参数的方法
; - Terminated:终止
// run正常退出;产生没有捕获到的异常
。
12.3 线程属性
中断线程: t.interrupt(); Thread.currentThread().isInterrupted();
如果线程被阻塞 sleep(); wait();
,无法检查中断状态,引入InterruptedException异常 catch (InterruptedException e){}
守护线程: t.setDaemon(true); // 为其他线程提供服务,如计时器
线程名:t.setName("name");
12.4 同步
竞态条件:两个及以上的线程共享同一数据的存取,若同时修改,两个线程会相互覆盖。
锁对象:ReentrantLock类
// 重入锁(需保证访问同一个锁对象)
var myLock = new ReentrantLock();
myLock.lock();
try
{
...
}
finally
{
myLock.unlock();
}
条件对象:管理获得了锁却不能工作的线程
Condition name = myLock.newCondition();
name.await(); // 阻塞, 放弃已获得的锁, 进入等待集
name.signalAll(); // 解除所有等待线程的阻塞
synchronized关键字(同步方法):一个方法声明后,对象的锁将保护整个方法(线程调用其必须获得内部对象锁,每个Java对象都有一个锁)
public synchronized void method()
{
...
wait(); // 将线程添加到等待集
notifyAll(); // 解除等待线程的阻塞
// 等价于上述的条件对象
}
同步块:获得Java对象的锁
synchronized(obj)
{
... //
}
监视器
- 只包含私有字段的类;
- 监视器类的每个对象都有一个关联的锁;
- 所有方法由这个锁锁定;
- 锁可以有任意多个相关联的条件。
Java对象
- 字段不要求是private;
- 方法不要求是synchronized;
- 内部锁对客户是可用的。
volatile:为实例字段的同步访问提供了一种免锁机制,编译器和虚拟机知道该字段可能被另一个线程并发更新。public volatile boolean done; // 不提供原子性
原子性:java.util.concurrent.atomic
提供机器级指令保证其他操作的原子性
死锁
线程局部变量
// 为每个线程构造一个实例
public static final ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
String dateStamp = dateFormat.get().format(new Date());
废弃方法
stop:终止所有未结束方法,造成对象处于不一致状态;suspend:会挂起一个持有锁的线程,在其恢复前该锁不可用。
12.5 线程安全的集合
阻塞队列:LinkedBlockingQueue LinkedBlockingDeque ArrayBlockingQueue PriorityBlockingQueue DelayQueue
// 有必要时全会阻塞
put // 如果队列满,则阻塞
take // 如果队列空,则阻塞
add // 如果队列满,抛出IllegalStateException异常
remove // 如果队列空,抛出NoSuchElementException异常
element // 如果队列空,抛出NoSuchElementException异常
offer // 如果队列满,则返回false
peek poll // 如果队列空,则返回null (因此,这些队列不能插入null)
映射条目的原子更新
import java.util.concurrent;
ConcurrentHashMap // 不允许有null
map.merge(key, 1L, Long::sum);
ConcurrentSkipListMap
ConcurrentSkipListSet
ConcurrentLinkedQueue
并发集视图:Set<String> words = ConcurrentHashMap.<String>newKeySet();