线程不安全性

先来举例说明线程不安全是什么情况下发生的:例如一个变量可以被多个线程进行访问,那么在大量线程并发访问这个变量的情况下,线程执行的顺序会给最后的结果带来不可预估的错误。

先定义一个单例类SimpleWorkingHardSingleton:

public class SimpleWorkingHardSingleton {
private static SimpleWorkingHardSingleton simpleSingleton = new SimpleWorkingHardSingleton(); // 数量
private int count; private SimpleWorkingHardSingleton() {
count = 0;
} public static SimpleWorkingHardSingleton getInstance() {
return simpleSingleton;
} public int getCount() {
return count;
} public void addCount(int increment) {
this.count += increment;
System.out.println(this.count);
} }

可以看到下面这个单例若在多线程环境下运行,count是被多个线程同时操纵的变量,示例:

for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
SimpleWorkingHardSingleton simpleSingleton = SimpleWorkingHardSingleton.getInstance();
simpleSingleton.addCount(1);
}
}).start();
}

看输出结果(你的执行结果可能和我的不同):

3
2
2
4
5

匪夷所思的结果,想不懂为什么会有两个2出现,1哪儿去了,为什么输出不是 1 2 3 4 5,下面就来解释一下

  1. 为什么没有1?

    可能是a线程算出count=1,然后输出count的时候,此时a线程挂起,b线程执行,b线程对count自增,此时a线程再输出的时候,count已经发生了变化,这就导致了1没有被输出
  2. 为什么两个2?

    可能是a,b两个线程都完成了count的计算,然后a线程输出,输出结束后立即被挂起,然后紧接着b线程立即也进行了输出,那么此时a b线程一定是输出了相同的count,就导致了相同值的出现。
  3. 将循环次数加大到100或者200,就会发现最后输出的count并不会到100或者200

    这是由于count++这个操作也不是原子的,也就是说count++并不是一次性完成,而是分为3步骤。第一步,获取count;第二步,给count指向的值加1;第三步,讲计算结果写回count。因此如果三个步骤再混合上多线程执行顺序的错乱因素,就会导致不可预测的问题了。比如a线程获取count为1,此时a线程立马被挂起,b线程获取count也为1,然后a,b线程各自去执行,最后写回count都是2,这就导致了count被少加一次。

线程安全性

其实我们看到线程安全性的定义的关键点在于正确性,即在多线程的环境下,无论运行时候环境采用如何的调度方式,系统或者类或者方法总能表现出预期的相符的行为,那么就是线程安全的。

总结

  1. 在多线程环境下,之所以会出现并发的线程安全性问题,是由于多个线程去操纵一个共享的变量或者一组变量,而且变量的操作过程不是原子的,那么线程的执行顺序就会干扰到变量。
  2. 为了保证线程安全性,解决方法:
    • 多个线程访问一个不可变量
    • 变量不可以被多线程共享
    • 线程做同步处理(原子性处理)

参考内容

  1. 书籍《Java并发编程实战》

Java并发(一)-了解线程安全的更多相关文章

  1. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  2. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  3. Java并发编程:线程池的使用(转)

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  4. Java并发编程:线程控制

    在上一篇文章中(Java并发编程:线程的基本状态)我们介绍了线程状态的 5 种基本状态以及线程的声明周期.这篇文章将深入讲解Java如何对线程进行状态控制,比如:如何将一个线程从一个状态转到另一个状态 ...

  5. Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  6. (转)Java并发编程:线程池的使用

    背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...

  7. Java并发编程:线程池的使用(转载)

    转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  8. Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程

    Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程 一.捕获异常并重新启用线程 public class Testun { public static voi ...

  9. Java并发编程:线程池的使用(转载)

    文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  10. [转]Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

随机推荐

  1. 2018.06.28 BZOJ1014 [JSOI2008]火星人prefix(非旋treap+hash)

    [JSOI2008]火星人prefix Time Limit: 10 Sec Memory Limit: 162 MB Submit: 8951 Solved: 2860 Description 火星 ...

  2. 多块盘制作成一个lvm

    1.创建pv[root@autodeploy nfs]# pvcreate /dev/vdd1WARNING: xfs signature detected on /dev/vdd1 at offse ...

  3. gj12-1 协程和异步io

    1 并发.并行.同步.异步.阻塞.非阻塞 并发.并行 并发是报一个时间段内有几个程序在同一个cpu上运行,但是任意时刻只有一个程序在cpu上运行.在一个时间段内某一个请求很快,能够响应的用户就越多,高 ...

  4. [笔记]linux命令学习

    scp /root/Downloads/cymothoa-1-beta.tar.gz root@192.168.1.66:/root/ rc.local exit 0前加入: sh /root/abc ...

  5. 第二届普适计算和信号处理及应用国际会议论文2016年 The 2nd Conference on Pervasive Computing, Signal Processing and Applications(PCSPA, 2016)

    A New Method for Mutual Coupling Correction of Array Output Signal 一种阵列输出信号互耦校正的新方法 Research of Robu ...

  6. (网络流) Island Transport --Hdu -- 4280

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=4280 源点是West, 汇点是East, 用Dinic带入求就好了 代码:要用c++提交 #pragma ...

  7. hdu 5019

    http://acm.hdu.edu.cn/showproblem.php?pid=5019 给出X 和Y,求出第K 大的 X 和Y 的公约数. 暴力求出所有公约数 #include <cstd ...

  8. 对SpringDAO层支持的总结

    1.问题 1.JDBC/ORM框架(如Hibernate)开发中编程模型有哪些缺点?  如JDBC   2.解决方案(模板设计模式,本质:将可变的和不可变的分离) 模板方法模式:定义操作的步骤(固定的 ...

  9. [mobile]移动端页面没有重新请求时,刷新页面代码

    <input type="hidden" value="yes" id="id_if_reload" /> <script ...

  10. Oracle EBS 采购 接收入库 接口开发

    http://blog.itpub.net/25164132/viewspace-746657/ 接收入库是项目中会经常碰到的开发,这类开发一般来说比较简单,但是接收入库在Oracle中其实涉及到很多 ...