线程安全问题的原因和解决方案
一、首先产生线程安全的原因:
(一)站在开发者的角度看:①多个线程之间操作同一个数据②至少有一个线程修改这个数据(不是读操作而是写操作)
(二)站在系统的角度看:一条语句对应多个指令,线程调度可以发生任何时刻(线程调度不会切割指令),所以线程调度不确定性导致指令执行顺序不确定,导致问题。
(三)线程不安全表现为三个部分:①原子性被破坏②内存可见性问题③代码重排序问题
一、原子性:
列如:int r=0;r++; r--; r++和r--应该是原子性的,但实际执行起来不能确保原子性,所以预期结果r=0,但可能最终结果为1。
如图:
二、内存可见性:一个线程对数据的处理,有可能其他线程无法感知,甚至某种情况下被优化的完全看不见。
把一个线程想象成一个cpu
内存分为:主内存(实际内存),工作内存(cpu缓存中的模拟)
线程对数据所有的读写操作:①从主内存中加载到工作内存中②工作内存进行处理,可以允许工作内存操作很长时间③完成工作后,把数据同步到主内存中
如班费花销,班上有个记录本(用来记录班费的总额),团支书拿着一部分班费去买班级用的东西,团支书脑子(工作内存)知道已经花销了,但还没有在记录本记录(主内存),班长去按照记录表上的班费去购买物品,会出现花费超值的现象。
三、代码重排序:我们写的程序,中间经过很多优化,导致不能确保最终执行的语句和我们写的语句顺序不一样。jvm中要求单线程没有这种变化导致的出错,但多线程可能会。
如图所示:A,B为两个线程,最后导致出错。
一点小小的知识点:Vector Stack Dictionary StringBuffer 是线程安全的,linkedList, ArrayList,hashMap StringBuilder没有线程安全。
二、解决线程安全的方法:锁(理论是数据,多个线程共用的数据)
加锁和解锁就是确保不是多个线程同时使用操作同一个数据,导致线程不安全。
锁有两种:synchronized 和lock
一、synchronized(同步锁)
synchronized{临界区代码},synchronized是对临界区代码加锁,不在大括号内的不加锁。
所谓加锁就是以下理论:一个线程持有锁(加锁成功),其余加锁失败的线程,放弃cpu进入堵塞队列(运行->堵塞)。打个比方一个房间只能一次进一个人,一个人进去了上锁(加锁成功),直到那个人出来之前其他人在外面等着(堵塞)。
怎么确保线程安全,就是在synchronized锁基础上,产生互斥。
互斥的条件:①都有加锁操作②都申请同一把锁
有以下代码:确定是不是互斥
首先我们要知道以下知识:
①synchronized void method(){ } 等价于 void method(){ synchronized (this){} }
②static synchronized void method(){} 等价于 static void method(){synchronized (类.class){} }
③synchronized 修饰普通方法 ,视为对“当前对象”(哪个对象调用的这个同步方法)加锁 即synchronized(this){}
④synchronized修饰静态方法视为所在类加锁 即 synchronized(类.class){}
有s1=new SomeClass(); s2=new SomeClass(); s3=s1;三个对象
如图所示以下线程是否互斥情况:
看一些特别的列子:s1.m1() 线程 是对普通方法加锁 加锁对象(对普通方法加锁是为谁调用它的对象即是s1) 即与s3.m1()是互斥的。 s1.m2() 对静态方法加锁 加锁对象(对静态方法加锁是所在类加锁即为someclass.class)即与s1.m5()是互斥的。
如果synchronized(ref){} ref为null 一定有 NullPointerException异常。
二、Lock是java.util.concurrent.locks.Lock的接口
Lock有以下方法:①lock()加锁 ②lockInterruptibly()加锁,但可以被中断
③tryLock(), tryLock(long time,TimeUnit unit)尝试加锁,返回没有加锁上false
④unlock()解锁
有个实现类为 ReentrantLock 即有以下模块:
Lock lock =new ReentrantLock();
lock.lock();//加锁
try{//临界区代码
}finall{
lock.unlock();
}放在finall中,确保在任何情况下都可以解锁
三、解决线程安全还有:volatile ,堵塞队列BlockingQueque<>接口,wait和notify
一volatile作用:
①volatile 主要作用是保护变量的可见性
②long a=100; double a=100; 高32位+低32位分别写入,不是原子的,即volatile保护了原子性
volatile long a=100;volatile double a=100;
③SomeObject s=new SomeObject();保证对象实例化正确
1.堆里申请内存空间,初始化为0x0
2.对象初始化工作:构造代码块,属性的定义时初始化,构造方法(这才算是一个正确对象)
3.赋值给s
volatile禁止重排序,只能1->2->3,如果1->3->2在3到2之间有线程(线程调度随机)使用对象,其对象是错的即出现问题。
能准确的表明其作用是单列模式:
单列模式分为饿汉模式(在类加载期间就进行对象实例化),懒汉模式(第一次用到时进行对象的实例化)
其懒汉模式实现如下:假如多个线程走先判断对象没有实例化,对类加锁(一个线程持有锁,但这是不知道是否实例化),所以要再判断是否实例化,没有实例化进行实例化,实例化了就返回对象,这里volatile就是要确保实例化正确。
二、bockingqueque<>方法有:①put(e)加入 take()取出 ②超时的情况offer(e,time,until)poll(time,until)
三、wait()(等待)和notify()(唤醒)是Object类,意味着java中所有对象都有着两种方法。wait和notify必须对等待对象进行synchronized加锁。
wait()唤醒的情况有:①notify()/notifyAll()方法唤醒 ②线程被中止 ③超时() ④假唤醒
线程安全问题的原因和解决方案相关推荐
- 线程安全问题的原因及解决方案
线程安全问题:简单来说,就是在多线程的调度下,导致出现了一些随机性,随机性使代码出现 了一些bug =>线程不安全 造成线程不安全的原因有哪些呢? 1)抢占式执行,调度过程随机(也是万恶之源,无 ...
- 线程安全问题的 3 种解决方案!
作者 | 磊哥 来源 | Java面试真题解析(ID:aimianshi666) 转载请联系授权(微信ID:GG_Stone) 线程安全是指某个方法或某段代码,在多线程中能够正确的执行,不会出现数据不 ...
- 线程安全问题及其解决方案
一:线程安全问题的原因: 1.线程是抢占式执行的,即具有随机性 2.多线程的情况下,线程同时去修改同一个数据或者有的线程在修改数据,有的线程在读取数据 3.一条语句对应多个指令,这些指令操作并不是原子 ...
- 多个线程直接按对数据进行操作容易引发线程安全问题
参考链接:http://www.cnblogs.com/paddix/p/5374810.html 代码: public class Count {public int num;public void ...
- 使用同步机制解决线程安全问题
线程的生命周期: 什么情况会产生线程安全问题? 当多个线程同时共享同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,即产生线程安全问题.读的操作不会发生线程安全问题. 例子:上文中的卖票 ...
- 多线程中的线程安全问题
多线程中的线程安全问题 概述: 多条线程在操作同一个资源的时候发生的数据交叉问题就是线程安全问题 产生原因: 多条线程操作同一个资源 解决思路: 要线程排队解决安全问题,设定权限. 如何设 ...
- Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字
线程安全问题是多线程编程中最典型的一类问题之一.如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的. 通俗来说,线程不安全指的就是某一代 ...
- java 全局变量线程安全_Java线程安全问题指的是全局变量,还是静态变量?
这个问题的答案是静态变量和全局变量都可能引起线程安全问题.这两种变量引起线程安全问题的原因和区别如下: 1.静态变量 静态变量即静态成员变量.只要有修改变量值的操作,无论是在单例或者非单例都是线程不安 ...
- iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(上)
2017-07-08 remember17 Cocoa开发者社区 目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 ...
最新文章
- tomcat5配置常见数据库连接池的例子.
- 在ASP.NET MVC 模型中 选择最好的方法将多个model(数据模型)传递到视图
- Python 一次for遍历多个列表及遍历时获取index
- JDK 8 新特性 之 Strams简单使用
- CF464E-The Classic Problem【最短路,主席树】
- ckeditor 框架分析 几个核心“人物”
- mysql在空闲8小时之后会断开连接(默认情况)
- 力扣190.颠倒二进制数
- MySQL error writing file 错误
- 如何判断对方列表里是不是好友_微信如何快速查看是否为好友关系
- 系统调用跟我学(4)
- angular 倒计时
- 系统调用之lseek
- 查看页面密码框明文密码
- 分析社会热点与网络营销的关联
- 1.3亿元战略投资落地,有赞的盈利拐点到了吗?
- freeswitch呼叫中心之百度MRCP语音合成识别环境搭建
- 手牵手带你安装zookeeper
- 老五苏-34:慧眼独具 比起“大哥”苏-27,苏-34的个子要高大一些。
- 手游客户端开发招聘要求