示例
如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。
以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值有可能小于 1000。
public class ThreadUnsafeExample {
private int cnt = 0;
public void add() {
cnt++;
}
public int get() {
return cnt;
}
public static void main(String[] args) throws InterruptedException {
final int threadSize = 1000;
ThreadUnsafeExample example = new ThreadUnsafeExample();
final CountDownLatch countDownLatch = new CountDownLatch(threadSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 1, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < threadSize; i++) {
executor.execute(() ->{
example.add();
countDownLatch.countDown();
});
}
countDownLatch.await();
executor.shutdown();
System.out.println(example.get());
}
}
// 输出结果
998 //如果线程是安全的最终结果就是 1000
解决线程安全问题
使用 AtomicInteger
(实际上非阻塞同步)
public class ThreadSafeExample {
private AtomicInteger cnt = new AtomicInteger(0);
public void add() {
cnt.incrementAndGet();
}
public int get() {
return cnt.get();
}
}
使用 synchronized
(实际上是阻塞同步/互斥同步)
public class ThreadSafeExample {
private int cnt = 0;
public synchronized void add() {
cnt++;
}
public synchronized int get() {
return cnt;
}
}
使用 Reentrant
(实际上是阻塞同步/互斥同步)
public class ThreadSafeExample {
private int cnt = 0;
private ReentrantLock lock = new ReentrantLock();
public void add() {
lock.lock();
try {
cnt++;
} finally {
lock.unlock();
}
}
public int get() {
lock.lock();
try {
return cnt;
} finally {
lock.unlock();
}
}
}