关闭

java多线程编程-构建自定义的同步工具

标签: 多线程
46人阅读 评论(0) 收藏 举报
分类:

一、条件队列
1. 标准形式:

void stateDependentMethod() throws InterruptedException{
    synchronized(lock) { //首先要持有锁
        while(!conditionPredicate()) //在唤醒后需再次确认,以防止在唤醒和获取锁之间的时间状态再次改变
            lock.wait(); //wait会释放锁,等待唤醒。在唤醒前,wait会尝试去获取锁,和普通调用竞争优先级相同
    }
}

Ex.

public class BoundedBuffer<V> extends BaseBoundedBuffer<V>{
    public BoundedBuffer(int size) { super(size); }

    public synchronized void put(V v) throws InterruptedException{
        while(isFull()){
            System.out.println(Thread.currentThread().getName()+ " put我睡了");
            wait();
            System.out.println(Thread.currentThread().getName()+ " put醒来了");
        }
        doPut(v);
        notifyAll();
    }

    public synchronized V take() throws InterruptedException{
        while(isEmpty()){
            System.out.println(Thread.currentThread().getName()+ " take我睡了");
            wait();
            System.out.println(Thread.currentThread().getName() + " take醒来了");
        }
        V v = doTake();
        notifyAll();
        return v;
    }

    public static void main(String[] args) throws InterruptedException{
        final BoundedBuffer<Integer> buf = new BoundedBuffer<Integer>(10);
        new Thread(new Runnable(){
            public void run(){
                try {
                    buf.take();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable(){
            public void run(){
                try {
                    buf.take();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        Thread.sleep(2000);
        new Thread(new Runnable(){
            public void run(){
                try {
                    buf.put(1);;
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

/** 上面的代码输出结果为 :
* Thread-0 take我睡了
* Thread-1 take我睡了
* Thread-1 take醒来了
* Thread-0 take醒来了
* Thread-0 take我睡了
**/

由于Thread-0 获取锁时没有竞争过Thread-1 ,所以Thread-1先运行,当Thread-0获取到锁时,经过判断条件,发现数组又是空了,所以又睡了。

  1. 使用条件队列,必须要满足 锁、条件谓词和条件变量之间的三元关系
  2. 使用显式条件变量的有界缓存
@ThreadSafe
public class ConditionBoundedBuffer<T>{
    protected final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    @GuardedBy("lock")
    private T[] items = (T[]) new Object[BUFFER_SIZE];
    @GuardedBy("lock")
    private int head,tail,count;

    public void put(T item) throws InterruptedException{
        lock.lock();
        try{
            while(count == items.length)
                notFull.await();
            items[tail] = item;
            if(++tail == items.length)
                tail = 0;
            ++count;
            notEmpty.signal();
        }finally{
            lock.unlock();
        }
    }

    public T take() throws InterruptedException{
        lock.lock();
        try{
            while(count == 0)
                notEmpty.await();
            T x = items[head];
            items[head] = null;
            if(++head == items.length)
                head = 0;
            -- count;
            notFull.signal();
            return x;
        }finally{
            lock.unlock();
        }
    }
}

4.信号量与lock
信号量与lock很类似,比如lock的lock类似于semaphore的acquire,lock的unlock类似于semaphore的release。其实它们(包括很多同步类)都是基于AQS(AbstractQueuedSynchronizer)

//使用锁实现信号量
@ThreadSafe
public class SemaphoreOnLock{
    private final Lock lock = new ReentrantLock();
    private final Condition permitsAvailable = lock.newCondition();
    @GuardedBy("lock") private int permits;

    public SemaphoreOnLock(int initPermits){
        lock.lock();
        try{
            permits = initPermits;
        }finally{
            lock.unlock();
        }
    }

    public void acquire() throws InterruptedException{
        lock.lock();
        try{
            while(permits == 0)
                permitsAvailable.await();
            --permits;
        }finally{
            lock.unlock();
        }
    }

    public void release() throws InterruptedException{
        lock.lock();
        try{
            ++permits;
            permitsAvailable.signal();
        }finally{
            lock.unlock();
        }
    }
}

5.AbstractQueuedSynchronizer

//使用AQS实现二元闭锁
@ThreadSafe
public class OneShotLatch{
    private final Sync sync = new Sync();

    public void signal() {sync.releaseShared(0);}

    public void await() throws InterruptedException{
        sync.acquireSharedInterruptibly(0);
    }

    private class Sync extends AbstractQueuedSynchronized{
        protected int tryAcquiredShared(int ignored){
            return (getState() == 1) ? 1 : -1;
        }
        protected boolean tryReleaseShared(int ignored){
            setState(1);
            return true;
        }
    }
}

实现原理:
sync.releaseShared(0) 实现源码是:

public final boolean releaseShared(int arg) {
            if (tryReleaseShared(arg)) {  //在这个例子中仅仅将可用状态设为1,并返回true
            doReleaseShared();
            return true;
        }
        return false;
    }

doReleaseShared()即对于链表中待唤醒的线程(即state的值为-1,全部唤醒),实现如下:

private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {  
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) // 将链表中所有值为-1的线程唤醒
                        continue; // loop to recheck cases
                        unparkSuccessor(h);  //唤醒关键步骤,使用unpark方法
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue; // loop on failed CAS
            }
            if (h == head) // loop if head changed
               break;  //双向链表 
        }
    }

LockSupport .park()和 LockSupport .unpark()实现线程的阻塞和唤醒 的
park的小例子:http://www.tuicool.com/articles/MveUNzF

sync.acquireSharedInterruptibly(0)的实现源码是:

 public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())  //响应中断
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

doAcquireSharedInterruptibly一旦链表的首节点状态发生改变,就通知第二个节点,并且通知与之shared的节点(闭锁是共享锁)。源码如下:

private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //将当前线程包装为类型为Node.SHARED的节点,标示这是一个共享节点。
        final Node node = addWaiter(Node.SHARED);  //添加新节点

        boolean failed = true;
        try {
            for (;;) {
            //如果新建节点的前一个节点,就是Head,说明当前节点是AQS队列中等待获取锁的第一个节点,
//按照FIFO的原则,可以直接尝试获取锁。
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                    //获取成功,需要将当前节点设置为AQS队列中的第一个节点,这是AQS的规则//队列头
                    //节点表示正在获取锁的节点
                        setHeadAndPropagate(node, r); 

                        p.next = null; // 将原先的头结点回收,help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) && //检查下是否需要将当前节点挂起
                    parkAndCheckInterrupt()) 
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

parkAndCheckInterrupt实现

 private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);   //等待获取权限,阻塞
        return Thread.interrupted();
    }
  1. ReentrantLock 实现
//非公平的ReentrantLock实现tryAcquire
//reentrantLock是独占锁
protected boolean tryAcquire(int ignored){
    final Thread thread = Thread.currentThread();
    int c = getState();
    if(c == 0){ //锁还未被占有
        if(compareAndSetState(0,1)){ // 防止锁的状态又发生更改
            owner = current;
            return true;
        }
    }else if(current == owner) {  //锁的占用者是自己
        setState(c+1);  //把次数加一
        return true;
    }
    return false; //获取失败
}
  1. Semaphore

信号量是共享锁
1. 计算剩余的许可数量
2. 如果没有足够的许可,翻译失败
3. 如果有剩余,采用compareAndSetState()以原子方法降低许可技术

protected int tryAcquireShared(int acquires){
    while(true){
        int available = getState();
        int remaining = available - acquires;
        //这样可以减少进行原子操作的次数
        if (remaining < 0 || compareAndSetState(available,remaining))
            return remaining;
    }
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:675次
    • 积分:103
    • 等级:
    • 排名:千里之外
    • 原创:10篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类