---
sidebar_position: 23
tags: [Language, Java, Concurrency]
---

# Concurrency

## Thread

线程: 分享 CPU、共享内存(多个线程访问同一数据/对象)

线程一般用于需长时间执行的任务: 循环、下载、浏览图片

### Create

创建线程的方法:

- 父类:继承 Thread 类, 重写 run 方法
- 接口:new Thread(Runnable Task)实现 Runnable 接口的类: 实名类/匿名类/Lambda 表达式

### Runnable

`thread.start()` 线程处于可运行状态: 可能在运行, 可能不在运行, 不必始终保持运行

### Block

- blocked
- waiting
- timed waiting

#### Blocked

获取锁不得时, 进入阻塞状态

#### Waiting

等待调度器时, 进入等待状态

#### Timed Waiting

调用含超时参数的方法时, 进入计时等待状态保持到超时或通知

`Object.wait`、`Thread.sleep`、`Thread.join`、`Lock.tryLock`、`Condition.await`

### Terminated

run 方法正常退出或抛出未捕获异常时, 进入(自然/意外)死亡状态

### 优先级

- 默认情况下继承父线程的优先级
- 需防止低优先级线程被饿死(因此不要依赖优先级进行编程)
- `MIN_PRIORITY`(1)~`NORM_PRIORITY`(5)~`MAX_PRIORITY`(10)
- 每当线程调度器选择新线程时,首选具有较高优先级的线程

### Daemon

- `Thread.setDaemon(true)`
- 设置为后台线程: 随时可能中断
- 虚拟机会在只有后台线程时退出,后台线程不可访问固有资源(文件、数据库等)

### 未捕获异常

实现 `Thread.UncaughtExceptionHandler` 接口.
不安装默认处理器时,默认处理器为空.

### Context

```java
public class UserContext implements AutoCloseable {
    static final ThreadLocal<String> ctx = new ThreadLocal<>();

    public UserContext(String user) {
        ctx.set(user);
    }

    public static String currentUser() {
        return ctx.get();
    }

    @Override
    public void close() {
        ctx.remove();
    }
}
```

## 中断

### Interrupt

对一个线程调用此方法时,线程将进入中断状态.

### Interrupted Exception

对一个阻塞线程(调用 sleep/wait 方法等)调用 interrupt 方法时, 抛出此异常.

## 同步

两个线程都有多个语句,
无法保证一个线程所有语句全部执行完再调用另一个线程,
必然会出现交错调用不同线程中的语句现象, 导致调用混乱现象.

### 锁对象

可重复(持有计数), 可共用(共用锁对象的方法可互相调用)

### 条件对象

管理有锁却不能正常工作的线程,
一个锁对象可以有多个相关的条件对象

### 内部锁

- synchronized 关键字
- 每个对象都有一个内部锁, 可将静态方法声明为 synchronized
- 等价于 `wait`/`notifyAll`
- 等价于 `await`/`signalAll`

```java
class TaskQueue {
    Queue<String> queue = new LinkedList<>();

    public synchronized void addTask(String s) {
        this.queue.add(s);
        this.notifyAll();
    }

    public synchronized String getTask() throws InterruptedException {
        while (queue.isEmpty()) {
            this.wait();
        }
        return queue.remove();
    }
}
```

### 读写锁

- 只允许一个线程写入 (其他线程既不能写入也不能读取)
- 没有写入时, 多个线程允许同时读 (提高性能)

```java
public class Counter {
    private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
    private final Lock rlock = rwlock.readLock();
    private final Lock wlock = rwlock.writeLock();
    private int[] counts = new int[10];

    public void inc(int index) {
        wlock.lock(); // 加写锁
        try {
            counts[index] += 1;
        } finally {
            wlock.unlock(); // 释放写锁
        }
    }

    public int[] get() {
        rlock.lock(); // 加读锁
        try {
            return Arrays.copyOf(counts, counts.length);
        } finally {
            rlock.unlock(); // 释放读锁
        }
    }
}
```

### Semaphore

```java
public class AccessLimitControl {
    // 任意时刻仅允许最多 3 个线程获取许可
    final Semaphore semaphore = new Semaphore(3);

    public String access() throws Exception {
        // 如果超过了许可数量, 其他线程将在此等待
        semaphore.acquire();
        try {
            // TODO:
            return UUID.randomUUID().toString();
        } finally {
            semaphore.release();
        }
    }
}
```

### Volatile

声明一个域可并发更新,通知编译器和虚拟器注意此特性.

### 死锁

所有线程处于等待或阻塞状态:
e.g. 两个线程互相等待状态.

### Thread Local Helper

## 线程安全

`java.util.concurrent` 并发 API: 线程安全 (同时只有一个线程调用某对象).

### 原子整数

```java
AtomicInteger.getAndIncrement();    // cnt++
AtomicInteger.getAndDecrement();    // cnt—
AtomicBoolean
AtomicLong
AtomicReference
```

### 集合

- 阻塞队列
- 高效映射表、队列
- 写数组列表和写数组集的拷贝 - `CopyOnWriteArrayList` 类、`CopyOnWriteArraySet` 类
- 同步包装器(synchronization wrapper) - 任何集合类通过同步包装器变成线程安全集合类

| interface | non-thread-safe            | thread-safe                                 |
| :-------- | :------------------------- | :------------------------------------------ |
| `List`    | `ArrayList`                | `CopyOnWriteArrayList`                      |
| `Map`     | `HashMap`                  | `ConcurrentHashMap`                         |
| `Set`     | `HashSet` /`TreeSet`       | `CopyOnWriteArraySet`                       |
| `Queue`   | `ArrayDeque` /`LinkedList` | `ArrayBlockingQueue` /`LinkedBlockingQueue` |
| `Deque`   | `ArrayDeque` /`LinkedList` | `LinkedBlockingDeque`                       |

## 异步计算

### 执行器

线程池 (创建大量生命周期短的线程):

- `CachedThreadPool`: 提交任务多, 创建新线程
- `FixedThreadPool`: 提交任务多, 等待当前任务完成再运行其他任务
- `SingleThreadExecutor`: 逐一执行提交任务
- `ScheduledExecutorService` 接口: 预定执行/重复执行任务

以上工厂方法返回: 实现 `ExecutorService` 接口的 `ThreadPoolExecutor` 对象

- 创建

```java
ExecutorService pool = Executors.newCachedThreadPool();
```

- 方法

```java
pool.execute(myTask);
pool.shutdown();
```

- Future 对象用于查询任务完成情况

## Swing

事件分配线程不应进行 input/output/sleep 调用(可能使线程阻塞)

- Timer 类(亦是线程): 每隔一段时间重复执行 `MyTask`
- 更新图形化界面: `SwingUtilities.invokeLater(Runnable MyTask)`

```java
EventQueue.invokeLater(new Runnable()
{
public void run()
{
    Statements;
}
});
```

- 指定布局 (Layout)

以下可合成一个方法:

- 创建组件(`Component`)
- 添加组件(`getContentPane.add(Component)`)
- 响应事件(`Event`)
- 设置属性(`size`, `location`, `bounds`, `locationByPlatform`, `title`, `iconImage`, `visible`, `resizable`, `undecorated`, `extendedState`)

```java
// 取得点击按钮的名字
String itemName = ((JRadioButton) e.getSource()).getText();
// Source: 事件源(点击按钮事件)
```
