并发实战值之----线程的安全性

来源:互联网 发布:javascript 语法 编辑:程序博客网 时间:2024/05/16 18:29

1,安全性??

(1)何谓线程安全?我的理解是,如果你的对象状态(实例变量或是静态变量)得到“正确”的转变,那么该对象就是线程安全的;

(2)所以

    如果你的对象是无状态的或者状态不可变的或者状态不共享的,那么它绝对是线程安全的;

如果你的对象的状态是共享的、而且是可变的,那么就必须保证你的“改变状态的代码”是原子性的,才能保证线程安全;

总之,线程不安全是因为存在“竞态条件”。

2,原子性:

(1)所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

(2)通俗的说法就是:一段代码要么全部执行,要么一条都不执行。

3原子性不可能由软件单独保证--必须需要硬件的支持,因此是和架构相关的。在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性

3,竞态条件:

(1)当两个或多个线程对共享的数据进行读或写的操作时,最终的结果取决于这些进程的执行顺序,就称存在“竞态条件”;也就是说不恰当的执行顺序可能会产生不正确的结果;

(2)竞态条件不一定总是会产生错误,还需要某种不恰当的执行顺序

(3)经典例子:

1)“先检查再执行”,如:延迟初始化

public class SingleTon {

private SingleTon(){};

private static SingleTon  instance = null;

public static void getInstance(){

if(instance != null){

instance = new SingleTon();

}

}

}

getInstance()方法中是先判断后初始化,可能会同时有多个线程同时判断为空,就出现了重复初始化;

2)操作(变量++):

它并不是一个原子的操作,即不是一个独立的操作,它分为三步执行:

      (1)读取变量的值

      (2)值加1

      (3)把值写回变量

即“读取---修改---写入”

可能会同时有多个线程同时执行(1),结果可想而知是不对。

4,复合操作:

1)即一组以原子方式操作的代码;

我们要使得竞态条件产生正确的结果,即线程安全的,那么就必须保证竞态条件是原子性的。

(2)javapackage java.util.concurrent.atomic;为我们提供了基本型数据的原子变量类,如AtomicLong,它内部提供的加、减等操作都是原子的。

5,加锁机制

(1)内置锁

Java中提供了一种内置的锁机制来支持原子性,也就是同步代码块(sysnchronized block),同步代码块包括两个部分:一个作为锁的对象引用和一个由锁保护的代码块;每个java对象都可以用作一个实现同步的锁,这些锁称为内置锁或是监视器锁;

(2)重入

我的理解就是线程可以再次获得已经持有的锁;

1)内置锁是可以重入的,即某个线程可以获得一个由它自己持有的锁;即当某个线程执行了类中的一个同步方法,那么它就可以执行该类的所有同步方法,以对象引用作为锁的同步块也一样;因为该线程持有了该类的对象引用作为的锁,即内置锁,而内置锁是可以重入的;

2)重入的一种实现方法是:为每个线程关联一个获取计数值和一个所有者线程;

原创粉丝点击