1. 定义
线程安全性最重要的概念就是正确性,而正确性的意思就是这个类表现的行为要和我们的期望的行为是一致的。
这个定义可能还会有些模糊,以下用一个例子来说明。
2. 例子
public class ThreadSafeDemo {
public int count = 0;
public void increaseCount() {
count++;
}
static class TaskThread extends Thread {
ThreadSafeDemo threadSafeDemo;
public TaskThread(ThreadSafeDemo threadSafeDemo) {
this.threadSafeDemo = threadSafeDemo;
}
@Override
public void run() {
for(int i = 0; i < 1000; i++) {
threadSafeDemo.increaseCount();
}
}
}
public static void main(String[] args) throws InterruptedException {
int num = 10;
ThreadSafeDemo threadsafe = new ThreadSafeDemo();
Thread[] threads = new Thread[num];
for(int i = 0; i < num; i++) {
threads[i] = new TaskThread(threadsafe);
threads[i].start();
}
for(int i = 0; i < num; i++) {
// 当线程数组的所有线程的任务执行完之后,主线程才会继续执行
threads[i].join();
}
System.out.println(threadsafe.count);
}
}
首先分析下我们期望这个类的行为是什么?从代码就可以看出,这里希望这个类的 count 变量可以从 0 变成 10000。
所以这里启动了 10 个线程,每个线程都会循环调用 1000 次 increaseCount(),这个方法就是调用 count++。
启动所有线程之后会循环调用每个线程的 join() 方法,目的就是为了阻塞主线程直到所有线程都执行完成之后才继续调用主线程的打印方法。
运行代码可能会出现如下结果:
9431
这个结果和我们预期代码输出的结果并不一致,这就代表这个类并不具有线程安全性了。
3. 什么时候才会讨论线程安全性?
要讨论线程安全性,必须满足两个前提条件:
- 在多线程的环境下
- 多个线程中对共享变量进行修改
如果多个线程根本没有共享变量或者没有对这个共享变量进行修改,这时候这个类肯定是线程安全的,所以根本没必要讨论这个类是否线程安全。