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

    在java平台出现很长一段时间内,开发多thread程序只能使用java平台提供的synchronized和volatile关键字,以及object类中的wait,notify和notifyall方法。以上抽象层次比较低,在开发中使用起来比较繁琐,并且容易产生错误。并且线程间交互方式存在某些固定的模式,好比生产者-消费者模式和读-写模式。后来J2SE5.0引入juc包,提供了高层次的API,能够知足平常开发中常见的需求。
java

    高级同步机制 
数据结构

    虽然非阻塞方式性能要优于阻塞方式,但并不是全部场景都要采用非阻塞的方式实现,有不少状况下仍然须要使用基于锁机制的阻塞方式实现。juc包的locks接口就是一个锁,能够经过其中的lock方法获取锁,unlock来解锁。使用lock接口的代码须要保证锁老是被释放,通常把unlock方法放在finally代码块中。lock方法获取锁的方式相似于synchronized关键词,以阻塞方式获取锁。另外还能够经过trylock方法以非阻塞方式获取锁。若是在调用trylock方法时没法获取锁,直接返回false,不会阻塞当前thread。利用trylock方法另一种重载形式能够指定超时时间。若是指定了超时时间,当没法获取锁时,当前thread会阻塞,但等待的时间不会超进指定的超时时间,同时thread也是能够被中断的。
性能

    另一个与锁相关的接口是ReadWriteLock.rwl接口实际上表示的是两个锁,一个是读取操做相关的锁,另外一个是写入操做使用的排他锁。该接口适合于解决常见的读取-写入问题相似场景。在没有thread进行写入操做时,进行读取操做的多个thread均可以获取读取锁,而进行写入操做的thread只有在获取写入锁后才能进行写入操做。多个thread能够同时进行读取操做,可是同一时刻只容许一个thread进行写入操做。在大多数状况下,对一个数据结构的读取操做次数要远多于写入操做的次数。
ui

    juc。locks包中提供lock接口和readwritelock接口的基本实现,分别为reentrantlock和reentrantreadwritelock类。这两具类共同特征是可重入性,即容许一个thread屡次获取同一个锁。reentranlock类对象能够有一个全部者thread,表示上一次成功获取该锁,但尚未释放锁的thread。reentrantlock类的对象同时保存了全部者thread在该对象上加锁的次数。经过getholdcount方法能够获取当前的加锁次数。若是reentrantlock类的对象当前没有全部者thread,则当前thread获取锁的操做会成功,加锁次数为1。在随后的操做中,thread能够再次获取该锁,这也是可重入的含义所在。每次加锁操做会使加锁次数加1,而每一次调用unlock方法释放锁会使加锁次数减1。当加锁次数变为0,该锁会被释放,能够被其余thread获取。
线程

    在建立reentrantlock类对象时能够经过一个额外的boolean类型参数来声明使用更加公平的加锁机制。在使用锁机制会遇到一个问题是thread饥饿问题。当多个thread同时竞争某个锁时,可能有的thread一直没法成功获取锁,一直处于没法运行状态。thread饥饿是有些程序应该避免的问题。若是在建立reentrantlock类的对象时添加了额外的参数true,则reentrantlock会使用相对公平的锁分配策略。当锁处于可被获取状态时,在因为尝试获取该锁而处于等待状态的thread中,等待时间最长的thread会成功获取这个锁。这就避免了thread饥饿问题。不带参数的trylock方法会忽略公平模式的设置。
code

    可重入锁优点在于减小了锁在各thread间传递次数,能够提升程序的吞吐量。为了提升程序总体吞吐量应该尽量使用可重入锁。lock接口代替synchronized,相对应的condition接口替代object类的wait,notify和notifyall方法。使用condition接口时也须要与一个对应的lock接口实现对象关联起来。经过lock接口的newcondition方法能够建立新的condition接口的实现对象。在调用condition接口的方法以前,也须要使用lock接口的方法来获取锁。condition接口提供了多个相似object类的wait方法的方法,最基本的是await方法,调用该方法会使当前thread进行等待状态,直到被唤醒或被中断。另一种await方法的重载形式能够指定超时时间。方法awaitnanos以纳秒数为单位指定超时时间,该方法返回值是剩余等待时间的估计值。相似的awaituntil方法也能够指定超时时间,只不过指定的不是要通过的时间而是超时发生的时间点,参数是一个java.util.date类的对象。前面几种方法都会响应其余thread发出的中断请求,而awaituninterruptibly方法则不会处理中断请求。
对象

    与condition接口中等待方法相对应的是signal和signalall方法,至关于object的notify和notifyall方法,示例
继承

Lock lock = new ReentrantLock();接口

Condition condition = lock.newCondition();队列

lock.lock();

try{

while(logic condition){

    condition.await();

}

}finally{

    lock.unlock();

}


底层同步器

    在一个multi thread中,thread 间可能存在多种不一样的同步方式,一种比较常邮的需求是对有限个共享资源的同步访问,multi thread程序中不少场景均可以抽象成这类同步方式。好比对某个监视器对象的互斥访问,其实是多个thread在竞争惟一的一个资源。若是系统中安装了两台打印机,那么须要进行打印操做的多个thread想互竞争这两个资源。当多个thread在等待同一个资源时,从公平的角度出发,这些thread会被放入到一个先入先出FIFO队列中,当资源变成可用时,处于队首的thread会获取该资源。

    若是程序中同步方式能够抽象成对有限个资源的同步访问,可使用juc。locks包中的abstractqueuesynchronizer类和abstractqueuedlongsynchronizer类做为实现的基础。这两个类的做用是相同的,只不过前者在内部使用一个int类型的变量来维护内部状态,然后者使用一个long类型的变量。能够将这个内部变量理解成共享资源的个数。经过getstate,setstate和compareandset这三个方法来更新这个内部变量的值。它们都是abstract的,所以须要继承并重写其中包含的部分方法后才以使用。一般作法是把它们的子类做为一个java内部类。外部的java类提供具体的同步方式。

public class SimpleResourceManager{
    private final InnerSynchronizer synchronizer;
    private static class InnerSynchronizer extends AbstractQueuedSynchronizer{
        InnerSynchronizer(int numOfResources){
            setState(numOfResources);
        }
        protected int tryAcquireShared(int qcquires){
            for(;;){
                int available = getState();
                int remaining = available - acquires;
                if(remaining < 0 || compareAndSetState(available,remaining)){
                    return remaining;
                }
            }
        }
        protected boolean tryReleaseShared(int releases){
            for(;;){
                int available = getState();
                int next = available + releases;
                if(compareAndSetState(available,next)){
                    return true;
                }
                
            }
        }
        //
    }
    public SimpleResourceManager(int numOfResources){
        synchronizer = new InnerSynchronizer(numOfResources);
    }
    public void acquire() throws InterruptedException {
        synchronizer.qcquireSharedInterruptibly(1);
    }
    public void release(){
        synchronizer.releaseShared(1);
    }
}
相关文章
相关标签/搜索