Java并发(一)-了解线程安全
线程不安全性
先来举例说明线程不安全是什么情况下发生的:例如一个变量可以被多个线程进行访问,那么在大量线程并发访问这个变量的情况下,线程执行的顺序会给最后的结果带来不可预估的错误。
先定义一个单例类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?
可能是a线程算出count=1,然后输出count的时候,此时a线程挂起,b线程执行,b线程对count自增,此时a线程再输出的时候,count已经发生了变化,这就导致了1没有被输出 - 为什么两个2?
可能是a,b两个线程都完成了count的计算,然后a线程输出,输出结束后立即被挂起,然后紧接着b线程立即也进行了输出,那么此时a b线程一定是输出了相同的count,就导致了相同值的出现。 - 将循环次数加大到100或者200,就会发现最后输出的count并不会到100或者200
这是由于count++这个操作也不是原子的,也就是说count++并不是一次性完成,而是分为3步骤。第一步,获取count;第二步,给count指向的值加1;第三步,讲计算结果写回count。因此如果三个步骤再混合上多线程执行顺序的错乱因素,就会导致不可预测的问题了。比如a线程获取count为1,此时a线程立马被挂起,b线程获取count也为1,然后a,b线程各自去执行,最后写回count都是2,这就导致了count被少加一次。
线程安全性
其实我们看到线程安全性的定义的关键点在于正确性,即在多线程的环境下,无论运行时候环境采用如何的调度方式,系统或者类或者方法总能表现出预期的相符的行为,那么就是线程安全的。
总结
- 在多线程环境下,之所以会出现并发的线程安全性问题,是由于多个线程去操纵一个共享的变量或者一组变量,而且变量的操作过程不是原子的,那么线程的执行顺序就会干扰到变量。
- 为了保证线程安全性,解决方法:
- 多个线程访问一个不可变量
- 变量不可以被多线程共享
- 线程做同步处理(原子性处理)
参考内容
- 书籍《Java并发编程实战》
Java并发(一)-了解线程安全的更多相关文章
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程控制
在上一篇文章中(Java并发编程:线程的基本状态)我们介绍了线程状态的 5 种基本状态以及线程的声明周期.这篇文章将深入讲解Java如何对线程进行状态控制,比如:如何将一个线程从一个状态转到另一个状态 ...
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- (转)Java并发编程:线程池的使用
背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...
- Java并发编程:线程池的使用(转载)
转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程
Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程 一.捕获异常并重新启用线程 public class Testun { public static voi ...
- Java并发编程:线程池的使用(转载)
文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- [转]Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
随机推荐
- centos_x64 6.4 安装jdk1.7
1.行到user目录下新建一个java目录 #cd /usr #mkdir java #cd /usr/java/ 2.下载jdk 先从oracle找到要下载的jdk地址然后 wget http:// ...
- linux下 C程序 参数和内存
#include <stdio.h> int main(argc, argv) int argc;char *argv[]; { printf("argc=%d \n&q ...
- cocos2d-x 3.4版本,videoPlayer和webView上添加sprite等cocos控件
本帖源于小弟自己在项目过程中为了解决在cocos2d-x中实现ios里videoPlayer自定义控件的需求,所以挖出来大神的一个帖子http://www.cocoachina.com/bbs/rea ...
- swiper3d横向滚动多张炫酷切换banner
最近有了个新需求,swiper3d横向滚动多张炫酷切换banner要和elementUI里边走马灯的卡片化card 类似,但是还需要h5手机触摸滚动啊啊啊啊,昨天折腾了半个早上总算完成,今天乖乖跑来m ...
- METAGENOMIC SEQUENCING ANALYSIS WORKFLOW
Metagenomics is defined as the study of the metagenome, which is total genomic DNA from environmenta ...
- hdu-2955(01背包+逆向思维+审题)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2955 思路:注意p和m[i]是被抓的概率,不能直接用,要转换为逃跑的概率,然后将得到的钱视为背包体积再 ...
- UVaLive 6525 Attacking rooks (二分图最大匹配)
题意:给定一个 n * n的图,X是卒, . 是空位置,让你放尽量多的车,使得他们不互相攻击. 析:把每行连续的 . 看成X集体的一个点,同理也是这样,然后求一个最大匹配即可. 代码如下: #prag ...
- SPSS—回归—曲线估计方程案例解析
上一节介绍了线性回归,虽然线性回归能够满足大部分的数据分析的要求,但是,线性回归并不是对所有的问题都适用, 因为有时候自变量和因变量是通过一个已知或未知的非线性函数关系相联系的,如果通过函数转换,将关 ...
- Yarn上运行spark-1.6.0
目录 目录 1 1. 约定 1 2. 安装Scala 1 2.1. 下载 2 2.2. 安装 2 2.3. 设置环境变量 2 3. 安装Spark 2 3.1. 下载 2 3.2. 安装 2 3.3. ...
- post同步请求
// http://api.hudong.com/iphonexml.do?type=focus-c //post请求中url不带请求参数,请求参数在参数HTTPBody中设置, 需要创建可变 ...