《java7核心技术与最佳实践》读书笔记之 multi-thread (2)

原创
2014/03/24 11:42
阅读数 636

基本thread的同步方式

    对于多thread中存在的数据竞争,需要利用java平台提供的同步机制来确保对共享变量的访问存在合适的“在之前发生”(happens-before)的顺序。java平台提供的基本同步方式包括synchronized关键词和object类中提供的wait,notify和notifyAll方法。

    synchronized,可以添加到方法或代码块之上。声明为synchronized的方法或代码块在同一时刻只能有一个thread允许访问。如果当前已有thread正在访问,那么其他试图访问该方法或代码块的thread会处于等待状态。所有的java对象都有一个与之关联的监视器对象monitor。允许thread在该monitor对象上进行加锁和解锁操作。jvm在锁被释放时,对共享变量的修改会从cpu缓存中直接写回到主存中,当锁被获取时,cpu的缓存中的内容被置为无效状态,直接从主存中重新读取共享变量的值。

    正确的使用synchronized是把方法中需要同步的代码用synchronized代码块包围即可。


    Object类的wait,notify,notifyAll方法

    synchronized关键字主要用来实现thread间互斥,即同一时刻只有一个thread允许执行特定代码。thread间也需要通过协作的方式来完成某些任务。在典型的生产者和消费者场景中。生产者thread在缓冲区已满时需要等待,等消费者thread从缓冲区中取走数据后再进行生产,消费者thread在缓冲区已空时需要等待,等生产者thread向缓冲区放入数据后再进行消费。这种协作方式可以抽象为等待-通知机制。在thread运行时可能需要满足某些条件才能继续进行。当条件不满足时,thread进入等待状态,等待其他thread运行而使条件得到满足。其他thread负责在合适的时机发出通知来唤醒处于等待状态的thread。可以用while循环和volatile变量来处理这个场景。但这种做法本质上是让thread处于忙等待状态,并通过轮询方式来判断条件是否满足。此时thread仍然需要战胜cpu时间,会对性能造成影响。更好的做法是使用Object的wait,notify,notifyAll方法。

    可以调用任意java对象的wait方法,java中每个对象除了有与之相关联的监视器对象之外,还有一个与之关联的包含thread的等待集合。在调用wait方法时,该方法调用的接收者所关联的监视器对象是所使用的监视器对象,同时wait方法所影响的是执行wait方法调用的当前thread。由于wait方法的成功调用需要当前thread持有监视器对象上的锁,因此wait方法调用需要放在使用synchronized声明的方法或代码块中。

    通过调用wait方法进行等待状态分为无超时和有超时两种。如果thread处于有超时等待状态,则thread可以被主动唤醒而离开等待状态之外,设定的超时时间过后也会自动离开等待状态。在设定超时时间时可以指定毫秒数和纳秒数。

    wait方法的作用是使当前thread进行等待状态。对应的notify,notifyall方法用来通知thread离开等待状态。notify会从该对象关联的等待集合中选择一个thread来唤醒。被唤醒的thread和其他thread竞争运行机会。notifyall方法会唤醒相关集合中所有threads。因此当等待集合中可能包含多个threads时,一般使用notifyall方法。

需要把wait方法调用置于循环之中。

synchronized(obj){

    while(/*logic condition 不满足*/){

    obj.wait();

}

}

线程状态:

    不同thread状态由枚举类型Thread.State来表示。可能过Thread类的getState方法来得到。它只表示jvm中thread的状态,并不表示对应os上thread的状态。

    1 NEW:thread刚被创建出来

    2 RUNNABLE:thread处于可运行状态。此时thread有可能正在运行,也有可能在等待其他资源中。

    3    BLOCKED:thread在等待一个监视器对象上的锁时,处于此状态。当一thread尝试执行声明为synchronized方法或代码块,又无法获取对应的锁时,处于此状态。

    4     WAITING:调用某些方法会使当前thread进入等待状态。这个等待没有超时时间,处于此状态的thread等待其他thread执行特定操作来使当前thread退出等待状态。

     5    TIMED_WAITING:此状态类似于4状态,但是增加了指定的超时时间。当超时时间过去,但thread等待的条件仍然没有发生,则thread也会退出等待状态。

    6 TERMINATED:thread的运行已终止。

    thread在同一时刻只能处于上述6种状态中的一种。

展开阅读全文
打赏
0
9 收藏
分享
加载中
laserdance博主
把数组对象的引用变量声明为volatile只能保证对这个引用变量本身的修改对其他线程可见,但是不涉及数组内所包含的元素。AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray类把volatile语义扩展到了数组元素的访问中。这三个类使用方式类似于对应的操作单个变量的类,只不过在方法调用中多了一个参数来指定要操作的数组元素的序号。
  最后一类java类通过反射方式对任何对象中包含的volatile变量使用comapreAndSet方法进行修改。AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater类分别用来对声明为volatile的int型,long型和对象引用变量类型进行修改。这三个类提供了一种方式把compareAndSet方法的功能扩展到在任何java类中声明为volatile的域上。这三个类在使用上虽然灵活,但在原子操作上所提供的语义较弱,因为除了这三个类外,还有可能有其他对象以其他方式对声明为volatile的域进行修改。
  从整体角度来说,juc包中java类属于比较底层的实现,一般juc包中很多非阻塞的数据结构的实现基础。实际上使用比较多的主要是AtomicBoolean,AtomicInteger,AtomicLong,AtomicREference这四个类。在实现线程安全计数时,AtomicInteger ,AtomicLong类是最佳选择。
2014/03/24 19:02
回复
举报
更多评论
打赏
1 评论
9 收藏
0
分享
返回顶部
顶部