线程不安全性

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

先定义一个单例类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. asp.net (jquery easy-ui datagrid)通用Excel文件导出(NPOI)

    http://www.cnblogs.com/datacool/archive/2013/03/12/easy-ui_datagrid_export_excel_asp_net.html

  2. 数据帮助类DBhelper的定义

    数据库帮助类的使用DBhelperusing System;using System.Collections.Generic;using System.Text;using System.Data;u ...

  3. 网段;IP;广播地址;子网掩码;

    网段(network segment)一般指一个计算机网络中使用同一物理层设备(传输介质,中继器,集线器等)能够直接通讯的那一部分.例如,从192.168.0.1到192.168.255.255这之间 ...

  4. Hadoop 1: NCDC 数据准备

    本文介绍Hadoop- The Definitive Guide一书中的NCDC数据准备,为后面的学习构建大数据环境; 环境 3节点 Hadoop 2.7.3 集群; java version &qu ...

  5. Java知识系列 -- 反射

    原理 要想理解 Java 反射,首先要弄清类的加载过程. 比如这行代码 Person p = new Person();. 我们想要创建一个 Person 对象,并用 p 作为对象的引用. 在 Jav ...

  6. IC向管理者角色转换

    1. 虽然你认为自己已经想明白怎么干,但还是从怎么干回归到要解决的问题,抛给正确的人(应该对这些问题负责的人),引导他们想出问题的答案. 给别人机会和空间,帮助他们成长: 人们对自己“想”出的方案更有 ...

  7. 2018.07.31cogs2964. 数列操作η(线段树)

    传送门 线段树基本操作. 给出一个排列b,有一个初始值都为0的数组a,维护区间加1,区间统计区间∑(ai/bi)" role="presentation" style=& ...

  8. python文件对比

    #-*- encoding:utf-8 -*- class loadDatas(object): def __init__(self): self.path='./data' def load_com ...

  9. 8b10b

    目的:保持直流平衡DC Balance). running disparity() 8bit原始数据会分成两部分,其低5位会进行5B/6B编码,高3位则进行3B/4B编码,这两种映射关系在当时已经成为 ...

  10. spring boot打包后windows启动乱码

    事情的起因什么的就不多表了,直接进入主题... 项目都要上线了,结果发现使用 idea mvn install之后的 jar在windows下启动乱码,而使用idea启动却没有问题!!! 这是神马情况 ...