代码示例: 创建线程计算 1 + 2 + 3 + … + 1000, 不使用 Callable 版本
static class Result {
public int sum = 0;
public Object lock = new Object();
}
public static void main(String[] args) throws InterruptedException {
Result result = new Result();
Thread t = new Thread() {
@Override
public void run() {
int sum = 0;
for (int i = 1; i <= 1000; i++) {
sum += i;
}
synchronized (result.lock) {
result.sum = sum;
result.lock.notify();
}
}
};
t.start();
synchronized (result.lock) {
while (result.sum == 0) {
result.lock.wait();
}
System.out.println(result.sum);
}
}
可以看到, 上述代码需要一个辅助类 Result, 还需要使用一系列的加锁和 wait notify 操作, 代码复杂, 容易出错.
代码示例: 创建线程计算 1 + 2 + 3 + … + 1000, 使用 Callable 版本
// 使用 Callable 版本实现 1 累加到 100
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建实现Callable接口的匿名内部类
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
};
// 2. 创建futureTask类 -- 要来存储callable的返回值
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread t = new Thread(futureTask);
t.start();
// 3. 调用futureTask的get方法 -- 该方法是阻塞等待的
Integer result = futureTask.get();
System.out.println(result);
}
理解Callable
可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全.
ReentrantLock 的用法:
ReentrantLock 和 synchronized 的区别:
如何选择使用哪个锁?
使用示例
原子类的应用场景
信号量的概念
Semaphore 使用示例
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(4);
int count = 0;
// acquire方法 -- P操作 -- 计数器减一
semaphore.acquire();
System.out.println(count++);
// release方法 -- V操作 -- 计数器加一
semaphore.release();
semaphore.acquire();
System.out.println(count++);
semaphore.acquire();
System.out.println(count++);
semaphore.acquire();
System.out.println(count++);
semaphore.acquire();
System.out.println(count++);
semaphore.acquire();
System.out.println(count++);
semaphore.acquire();
System.out.println(count++);
}
运行结果如下;
好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才能公布成绩。
应用场景举例
使用举例
public static void main(String[] args) throws InterruptedException {
// 构造方法中, 指定创建几个任务.
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
int id = i + 1;
Thread t = new Thread(() -> {
System.out.println("线程" + id + "正在工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程" + id + "完成工作");
// 每个任务执行结束这里, 调用一下方法
// 把 10 个线程想象成短跑比赛的 10 个运动员. countDown 就是运动员撞线了.
countDownLatch.countDown();
// 假设线程不退出
while (true);
});
t.start();
}
// 主线程如何知道上述所有的任务都完成了呢??
// 难道要在主线程中调用 10 次 join 嘛?
// 万一要是任务结束, 但是线程不需要结束, join 不就也不行了嘛?
// 主线程中可以使用 countDownLatch 负责等待任务结束.
// a => all 等待所有任务结束. 当调用 countDown 次数 < 初始设置的次数, await 就会阻塞.
countDownLatch.await();
System.out.println("多个线程的所有任务都执行完毕了");
}