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

原创 2017年01月03日 17:06:18

一、条件队列
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;
    }
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

java并发编程实践学习(14 ) 构建自定义的同步工具

一.管理状态依赖性在单线程化的程序中,如果调用一个方法时,依赖于状态的先验条件为满足(比如连接池非空),那么这个先验条件就无法变为真。但是在并发程序中,基于状态的先验条件会在其他线程的活动中被改变。对...

多线程编程入门(16):线程同步工具之CyclicBarrier

package cn.itcast.heima2; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Ex...

多线程编程入门(15):线程同步工具之Semaphore(信号量)

package cn.itcast.heima2; import java.util.concurrent.ExecutorService; import java.util.concurrent....

多线程编程入门(18):线程同步工具之Exchanger

package cn.itcast.heima2; import java.util.concurrent.Exchanger; import java.util.concurrent.Execut...

多线程编程入门(17):线程同步工具之CountDownLatch

package cn.itcast.heima2; import java.util.concurrent.CountDownLatch; import java.util.concurrent.E...

Java多线程编程2--同步锁定--volatile关键字、原子类

使用volatile关键字增加了实例变量在多个线程之间的可见性。但volatile关键字最致命的缺点是不支持原子性。     下面将关键字synchronized和volatile进行一下比较:   ...

Java多线程编程总结笔记——六线程的同步与锁

同步和锁定: 关于锁和同步,有一下几个要点: 1)、只能同步方法,而不能同步变量和类; 2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步? 3)、不...

【转载】Java多线程编程2--同步锁定--死锁

1、死锁实例    Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成。在多线程技术中,“死锁”是必须避免的,因为这会造成线程的“假死...

关于java 多线程编程的同步问题

说到线程编程,我们初步的认识,就是通过编程的方式让系统资源在不同的线程中切换,从而实现系统高效的运行。然后这看似简单明了的问题,实际操作起来,却隐藏了很多复杂的细节,其中线程间的同步和通信问题,就是导...

Java多线程编程--(3)线程互斥、同步的理解

from  http://blog.csdn.net/drifterj/article/details/7771230 多线程并行编程中,线程间同步与互斥是一个很有技巧的也很容易出错的地方。 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)