java程序中怎么保证多线程的运行安全_Java线程安全问答(草稿)

1.什么是线程安全?

如果一个类在多线程执行中,在不考虑运行环境的调度干预,也不需要调用代码的协调同步,仍然保证正确地运行,那么这个类就是线程安全的

也就是说,多线程环境下,线程安全的类总是有正确的行为。但是这种类在实际情况中是很少的。

实际情况下的类一般分为5个类别(Java Concurrency in Practice》的作者Brian Goetz给出):

不可变的 这篇文章比较详细的讲述了不可变性。另外设计模式中,还有一个不变模式。

绝对安全/无条件的线程安全,通常来讲这个类“怎么用怎么安全”,但我加了双引号,其实还是要注意的

相对安全/有条件的,方法单个使用是没问题的,但为了处理某些业务按照顺序连续调用需要同步

线程兼容/非线程安全,需要外部同步保证

线程对立,没法协调做到安全

这篇文章的最后给出了这5个级别的更多解释。

2.为什么也好关注线程安全?

随着软硬件的发展,Java代码运行在多处理器环境中。

2.1spring单例,tomcat线程池

spring容器中,类多是单例的。

而web服务器为了提高性能,都是用了线程池处理请求。

2.2提升效率,自己开启新线程

为了提升效率,

应用中也会存在开启多个线程分隔处理多个任务。

或者自己维护线程池处理异步任务的情况。

此时,多个线程的协调和交互都需要关注线程安全。

java中类中的属性会同时有多个线程访问。

3.为什么是类中的属性?方法参数不需要关注线程安全吗?

JVM内存结构图

0818b9ca8b590ca3270a3433284dd417.png

是属性还是方法参数取决于在jaa运行时共享的资源

这篇文章讲述了java运行时共享的那个资源。

这篇文章从java内存模型的角度分析java中属性和方法参数在底层的实现。

4.怎么才能保证线程安全呢?

需要保证 原子性,可见性,有序性。

5.什么是原子性?为什么要考虑原子性?

一个线程可能对某个属性有多个操作,比如i=i++。可以分解为2个动作

原子性就是这个操作在2步操作中是都必须要完成的,中间不能有其他线程的操作。对其他线程来说这个操作是排他的。

这个保证原子性的过程,需要锁的参与。除了排他锁,还有其他锁。

这篇文章http://ifeve.com/java_lock_see/ 讲的比较详细,也比较有难度。实际上java已经提供了锁的实现,直接使用即可。

加锁同时保证了有序性和可见性。

7,什么是可见性?为什么要考虑可见性?

可见性是值一个线程的操作结果对其他线程可见。

为什么会有不可见的情况呢?这是由java的内存模型决定的,java的内存模型是一个抽象的概念,其具体组成是由cpu缓存,指令缓存等构成的。

为了不受其他因素影响,提升效率,java为每一个线程分配了内存空间叫“工作内存”,执行过程中,

每个线程会从“主内存”拷贝一份数据到工作内存。并在工作内存中计算完成之后将计算结果回写到主内存。比如i++操作。

多个线程的工作内存是不可见的,只有在主内存的数据才可见。

java中的关键字volatile 抑制了一些线程运行上的优化,保证了可见性。比如对volatile变量的操作,任何线程的操作都不再拷贝数据到自己的工作内存,而是直接在主内存中操作。这样保证了多个线程的操作都是可见的,同时也保证了部分的有序性,但是降低了效率。

JVM中每个线程会有自己的栈,而堆是存放各线程所用对象的地方。堆类似于JMM中的主内存,栈中的一部分类似工作内存。

8 .什么是有序性?为什么要考虑有序性?

java为了线程的高效并发执行,也是为了配合工作内存或者说缓存的有效利用,除了工作内存机制的优化之外,还有“指令重排序”。所以需要考虑有序性。

有序性在多线程环境中比较复杂,有序性是指某个操作(操作B)的结果可能需要前一个操作(操作A)的结果前提下完成。

正确的结果应该是操作A-->操作B.不应该出现这种情况操作B-->操作A.

本线程内观察,线程内的所有操作有序,一个线程观察另外一个则无序。

多线程环境中保证有序性,需要遵循happens-before原则,这个原则是重排序的一个默认保证(这个如果不保证,则代码的执行结果不可预见,每次执行都可能不同,这是不可接受的):

Program order rule. 线程内的代码能够保证执行的先后顺序

Monitor lock rule. 对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前

Volatile variable rule. 保证前一个对volatile的写操作在后一个volatile的读操作之前

Thread start rule. 一个线程内的任何操作必需在这个线程的start()调用之后

Thread termination rule. 一个线程的所有操作都会在线程终止之前

Interruption rule. 要保证interrupt()的调用在中断检查之前发生

Finalizer rule. 一个对象的终结操作的开始必需在这个对象构造完成之后

Transitivity. 可传递性 9,这8条原则怎么理解呢?

可以参考

http://blog.163.com/javaee_chen/blog/static/179195077201131382128499/

http://ifeve.com/easy-happens-before/

http://javatar.iteye.com/blog/144763

10,双重检查锁定为什么会失效?

双重检查锁定是:

失效的原因主要有:

1.指令重排序

2.内存的可见性

http://ifeve.com/doublecheckedlocking/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值