容易忽略的知识
- 在32位系统中,未使用 volatile 关键字声明的 long/double 类型不是原子写操作。
- 在方法声明处添加 synchronized 关键字不是锁方法,而是锁当前类的对象。
- 创建太多的线程会导致程序执行效率降低。
线程的生命周期
图片来源《Java 多线程编程核心技术》
使用多线程
继承Thread类
public class XXXXThread extends Thread {
@Override
public void run() {
// TODO
}
public static void main(String[] args) {
XXXXThread thread = new XXXXThread();
thread.start();
}
}
实现Runnable接口
public class XXXXThread implements Runnable {
@Override
public void run() {
// TODO
}
public static void main(String[] args) {
Runnable runnable = new XXXXThread();
Thread thread = new Thread(runnable);
thread.start();
}
}
获取多个线程任务的结果
ExecutorService executorService = Executors.newFixedThreadPool(10);
CompletionService<Object> completionService = new ExecutorCompletionService<>(executorService);
// 定义任务
Callable<Object> callable = () -> {
long sleepTime = (long) (Math.random() * 5000);
Thread.sleep(sleepTime);
return sleepTime;
};
// 执行任务
for (int i = 0; i < 10; i++) {
completionService.submit(callable);
}
// 获取结果
for (int i = 0; i < 10; i++) {
System.out.println("result: " + completionService.take().get());
}
实用的线程类
ReentrantLock类
因为 synchronized 是Java语言层面提供的语法,所以我们不需要考虑异常,而 ReentrantLock 是Java代码实现的锁,我们就必须先获取锁,然后在 finally 中正确释放锁。
Condition类
Condition 提供的 await、signal、signalAll 原理和 synchronized 锁对象的 wait、notify、notifyAll 是一致的,并且其行为也是一样的。
ReadWriteLock类
只允许一个线程写入(其他线程既不能写入也不能读取)。没有写入时,多个线程允许同时读。
StampedLock类
StampedLock 提供了乐观读锁,可取代 ReadWriteLock 以进一步提升并发性能。StampedLock 是不可重入锁。
Future类
对线程池提交一个 Callable 任务,可以获得一个 Future 对象。可以用 Future 在将来某个时刻获取结果。
线程的状态
图片来源《Java 多线程编程核心技术》
状态 | 描述 |
---|---|
NEW | 至今尚未启动的线程 |
RUNNABLE | 执行的线程 |
BLOCKED | 受阻塞并等待某个锁的线程 |
WAITING | 等待另一个线程来执行某一特定操作的线程 |
TIMED_WAITING | 等待指定时间的线程 |
TERMINATED | 已退出的线程 |