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并发编程实战-构建自定义的同步工具

1,状态依赖性的管理:在单线程程序中,如果某一个基于状态的前提条件未得到满足,则这个条件将永远无法成真,可是,在并发程序中,基于状态的条件可能由于其他线程的操作而改变 2,条件队列:他使得一组线程能够...
  • tidu2chengfo
  • tidu2chengfo
  • 2017年07月23日 16:21
  • 90

Java并发编程实战(学习笔记 十三 第十四章 构建自定义的同步工具 上)

自定义的同步工具
  • ahaha413525642
  • ahaha413525642
  • 2017年08月15日 16:24
  • 122

java并发编程--自定义同步组件

concurrent包的组成看下结构图: 编写一个自定义同步组件来加深对同步器的理解业务要求: * 编写一个自定义同步组件来加深对同步器的理解。 * 设计一个同步工具:该工具在同一时刻,只允...
  • tianjun2012
  • tianjun2012
  • 2017年02月28日 15:58
  • 364

构建自定义的同步工具

1. 轮询 2. 休眠
  • z20071050210
  • z20071050210
  • 2014年07月24日 09:04
  • 167

Java多线程并发编程之构建自定义同步工具

当Java类库没有提供适合的同步工具时,就需要构建自定义同步工具。 可阻塞状态依赖操作的结构 acquir lock on object state;//请求获取锁 while(prec...
  • csujiangyu
  • csujiangyu
  • 2015年03月01日 14:12
  • 439

《java多线程编程核心技术》核心笔记(一)

线程具有优先级  线程的优先级具有继承特性。被调用者继承调用者的优先级   优先级高的线程 绝大部分会现执行 方法内的变量是线程安全的。 多个线程访问同一个对象的...
  • wangyan199366
  • wangyan199366
  • 2016年07月04日 11:21
  • 786

《Java多线程编程核心技术》(一)多线程技能

最近阅读了一本《Java多线程编程核心技术》,总结了一下每章的知识点:第一章,java多线程技能 知识点:1,实现多线程编程的方式主要有两种:一是继承Thread类,重新run方法,二是实现Runna...
  • wfzczangpeng
  • wfzczangpeng
  • 2016年09月20日 23:04
  • 983

《Java并发编程实战》第十四章 构建自定义的同步工具 读书笔记

一、状态依赖性的管理 有界缓存实现的基类 @ ThreadSafe public abstract class BaseBoundedBuffer { @GuardeBy( "thi...
  • love_world_
  • love_world_
  • 2014年06月03日 08:04
  • 1683

[Java Concurrency in Practice]第十四章 构建自定义的同步工具

构建自定义的同步工具类库中包含了许多存在状态依赖性的类,例如FutureTask、Semaphore和BlockingQueue等。在这些类的一些操作中有着基于状态的前提条件。例如,不能从一个空的队列...
  • qilixiang012
  • qilixiang012
  • 2015年09月09日 23:44
  • 519

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

一.管理状态依赖性在单线程化的程序中,如果调用一个方法时,依赖于状态的先验条件为满足(比如连接池非空),那么这个先验条件就无法变为真。但是在并发程序中,基于状态的先验条件会在其他线程的活动中被改变。对...
  • qq_27428109
  • qq_27428109
  • 2017年02月21日 15:06
  • 185
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java多线程编程-构建自定义的同步工具
举报原因:
原因补充:

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