
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
我们在上文中给大家简单介绍了程序员在学习Java编程线程的时候需要掌握的一些基础知识等内容,而本文我们就继续来学习一下,Java线程安全概念与防护方法。
一、线程安全
在没有充足同步的情况下,多个线程中的操作的执行顺序是不可预测的,会产生安全性问题。一开始就设计一个线程安全的类非常重要,因为这比以后再将这个类修改为线程安全的类要容易得多。
二、什么是线程安全性
线程安全性:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。
正确性的含义是,某个类的行为与其规范完全一致。
哪些是不安全的代码:
RaceCondition(竞态条件/竞争态势):并发编程中,由于多个线程不恰当的执行时序而出现不正确的结果。常见的racecondition比如:多线程执行count++,由于自增运算不是原子操作,它包含3个独立地操作:读取-修改-写入。线程读取到的数值可能是前面3个操作中的任意一步的值,所以它的终结果是不可预测的。
在没有同步的情况下,编译器、处理器和运行时等都可能对操作的执行顺序进行一些意想不到的调整,即“指令重排”。
非volatile的long和double变量,JVM允许将64位的读操作或写操作分解为两个32位的操作,即如果对该变量的读写操作在不同的线程中执行,那么很可能会读取到某个值的高32位和另一个值的低32位。因此在多线程程序中使用共享且可变的long和double变量是不安全的,保证其安全性需要加锁保护或者用volatile修饰。对象逸出:某个不该发布的对象被发布。
对象逸出导致其他线程拿到半成品对象的引用,从而引发安全性问题。
三、如何保证安全性
无状态对象一定是线程安全的。
加锁
每个Java对象都可以用作锁,它称为”内置锁“或监视器锁。内置锁是可重入的。synchronized修饰的方法,它用的锁就是方法所在的对象,静态的synchronized方法以Class对象作为锁。加锁也可以保证变量的内存可见性。
内存可见性
volatile变量,确保变量的更新操作被其他线程可见,是一种轻量级的同步机制(在大多数处理器架构上,读取volatile变量的开销只比读取普通变量略高一些)。
编译器和运行时都会注意到volatile变量是共享的,不会在该变量上做指令的重排序,也不会缓存该变量在寄存器或对其他处理器不可见的地方,因此volatile变量的新值总会被所有线程可见。
对象的安全发布
发布(Publish)一个对象是指,使对象能够在当前作用域之外的代码中使用。
逸出(Escape)指某个不该发布的对象被发布了。
发布内部状态可能会破坏封装性,并使程序难以维持不变性条件。
不变性条件(invariant):不同变量之间的约束关系。
前置条件(pre-conditions):在调用该方法或代码块之前,该条件必须为true;
后置条件(post-conditions):在调用该方法或代码块之后,该条件必须为true;
线程封闭
线程封闭(ThreadConfinement),是指不共享数据,线程独享一份数据。这是实现线程安全性简单的方式之一。
线程封闭技术的常见应用是JDBC的Connection对象,它不是线程安全的,但各个服务线程独享一个Connection对象。
栈封闭,是线程封闭的一种特例,只能通过局部变量才能访问对象。基本类型的局部变量始终封闭在线程内。
ThreadLocal类,是一种更规范的线程封闭方法。它用于保存一个线程独享的值。ThreadLocal提供了get和set方法。
使用不可变对象
不可变对象一定是线程安全的。满足以下条件时,对象才是不可变的:1.对象创建以后其状态就不能修改;2.对象的所有域都是final类型;3.对象是正确创建的(对象创建期间,this引用没有逸出)。
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。更多内容请加danei0707学习了解。欢迎关注“达内在线”参与分销,赚更多好礼。