写于:2020-01-09

一、线程安全与数据同步

当多个线程间存在共享的资源时,便会存在线程安全的问题。为了解决共享资源的安全问题,就需要解决共享资源在各个线程间的数据同步问题。

二、数据不一致

当多个线程对一个共享的数据进行操作的时候,就会出现数据不一致的问题。

1、简单案例引出线程安全问题。

案例:多个线程多一个数字进行累加,当累加到指定数量时,结束

  1. public class SimpleThread {
  2. public static void main(String[] args) throws InterruptedException {
  3. final TaskRunnable taskRunnable = new TaskRunnable();
  4. List<Thread> threads = IntStream.rangeClosed(1, 4).mapToObj(loopTimes -> {
  5. return new Thread(taskRunnable, "T" + loopTimes);
  6. }).collect(Collectors.toList());
  7. threads.stream().forEach(Thread::start);
  8. }
  9. public static class TaskRunnable implements Runnable{
  10. /** 共享的数据 **/
  11. private int index = 0;
  12. /** 结束累加的阈值 **/
  13. private static final int max = 15000;
  14. @Override
  15. public void run() {
  16. while(index <= max){
  17. System.out.println(Thread.currentThread() +":" + (index++));
  18. }
  19. }
  20. }
  21. }

多次运行可能出现问题如下

1、某个值重复出现

2、某个值被忽略没有出现

3、总数超过了最大值

2、问题分析

index++;不是一个原子操作;他分为三个部分 a、取值 b、累加 c、赋值

真个累加操作可以分为如下几个步骤:

  • 1、while 判断
  • 2、index 取值
  • 3、index 累加
  • 4、累加值 赋值给 index
  • 5、打印 index 值

某个值重复出现

两个线程,线程1和线程2。
02.jpg
线程 2 拿到 CPU 执行权进行操作: 1[while判断],2[index取值],3[index累加] 。然后释放了cpu执行权。

线程 1 拿到 CPU 执行权进行操作: 1[while判断],2[index取值],3[index累加],4[赋值],5[打印],此时打印结果 index =2。然后释放 cpu 执行权。

线程 2 拿到 CPU 执行权进行操作: 4[赋值],5[打印],此时打印结果 index =2,。

出现重复值打印问题。

某个值被忽略

两个线程,线程1和线程2。
01.jpg
线程 2 拿到 CPU 执行权进行操作:1[while判断],2[index取值],3[index累加],4[赋值]。然后释放 cpu 执行权

线程 1 拿到 CPU 执行权进行操作:1[while判断],2[index取值],3[index累加],4[赋值],5[打印],此时打印结果 index =3,然后释放 CPU 执行权。

线程 2 拿到 CPU 执行权进行操作:5[打印],此时打印结果为 3。

出现忽略问题,2的打印被忽略了。

超过最大值

两个线程,线程1和线程2。
03.jpg
最大值为 500.此时 index = 499。

线程 1 拿到 CPU 执行权进行操作:1[while判断],然后释放 CPU 执行权。

线程 2 拿到 CPU 执行权进行操作:1[while判断],然后释放 CPU 执行权。

线程 1 拿到 CPU 执行权进行操作:2[index取值],3[index累加],4[赋值],5[打印]。此时 index 为最大值 500.

线程 2 由于已经通过 1[while] 的判断,所以能够接着操作:2[index取值],3[index累加],4[赋值],5[打印],此时 index 超出最大值,结果为 501.

出现超过最大值问题。

三、问题总结

出现线程安全和数据不一致的问题,都是因为多个线程同时对一个共享资源同时进行操作造成的。