创建SingleBlockingQueue同步器

时间:2016-05-25 20:15:58

标签: java multithreading concurrency

我试图创建一个SingleBlockingQueue<T>同步器,允许一个线程offer()一个元素到它,另一个线程将take()它。 {1}}中一次只保留一个T元素,如果前一个元素正在等待获取线程SingleBlockingQueue<T>,则offer()上的推送线程被阻塞。推送线程将继续推送项目,直到它调用take(),并且当setComplete()为假时,获取线程将继续调用take()。如果正在等待元素,则获取线程将阻塞。

这是我到目前为止所拥有的同步器。

isComplete()

以下是Kotlin的一个用法示例

import java.util.concurrent.atomic.AtomicBoolean;

public final class SingleBlockingQueue<T> {

    private volatile T value;
    private final AtomicBoolean isComplete = new AtomicBoolean(false);
    private final AtomicBoolean isPresent =  new AtomicBoolean(false);

    public void offer(T value) throws InterruptedException {
        while (isPresent.get()) {
            this.wait();
        }
        this.value = value;
        synchronized(this) {
            this.notifyAll();
        }
    }
    public boolean isComplete() {
        return !isPresent.get() && isComplete.get();
    }
    public void setComplete() {
        isComplete.set(true);
    }
    public T take() throws InterruptedException {
        while (!isPresent.get()) {
            this.wait();
        }
        T returnValue = value;
        isPresent.set(false);
        synchronized(this) {
            this.notifyAll();
        }
        return returnValue;
    }
}

然而,我收到了一个错误,此时我有点过头了。由于RxJava,我很久没有制作同步器了。我究竟做错了什么?

    val queue = SingleBlockingQueue<Int>()

    thread {
        for (i in 1..1000) {
            queue.offer(i)
        }
        queue.setComplete()
    }

    thread {
        while (!queue.isComplete) {
            println(queue.take())
        }
    }

    Thread.sleep(100000)

3 个答案:

答案 0 :(得分:2)

您不需要自己实施,可以使用SynchronousQueue

参考文献:

SynchronousQueue javadoc

http://tutorials.jenkov.com/java-util-concurrent/synchronousqueue.html

  

SynchronousQueue类实现BlockingQueue接口。   有关该接口的详细信息,请阅读BlockingQueue文本。

     

SynchronousQueue是一个只能包含单个元素的队列   内部。将元素插入队列的线程被阻止   直到另一个线程从队列中获取该元素。同样,如果一个   线程尝试获取元素,当前不存在任何元素,   该线程被阻塞,直到一个线程插入一个元素   队列中。

答案 1 :(得分:1)

正如其他人所指出的那样,您可以使用SynchronousQueue中的现有实现。

如果你想要实现自己的,你只需要确保对wait()块的调用是在synchronized块内。

不幸的是,我认为原始代码中的isComplete() / setComplete()机制会受到竞争条件的影响,因为在setComplete()返回后isComplete()可能会被调用false 1}}以及在阅读线程执行take()之前或之后。这可能会使读取线程挂起。

  public final class SingleBlockingQueue<T> {
    private final Object lock = new Object();
    private T value;
    private boolean present = false;

    public void offer(T value) throws InterruptedException {
      synchronized (lock) {
        while (present)
          lock.wait();
        this.value = value;
        present = true;
        lock.notifyAll();
      }
    }

    public T take() throws InterruptedException {
      synchronized (lock) {
        while (!present)
          lock.wait();
        T returnValue = value;
        value = null; // Should release reference
        present = false;
        lock.notifyAll();
        return returnValue;
      }
    }
  }

为了进行比较,基于SemaphoreCondition对象实现此类队列可能更为自然。这是一个使用一对信号量来表示空/满状态的实现。

  public final class SingleBlockingQueue<T> {
    private volatile T value;
    private final Semaphore full = new Semaphore(0);
    private final Semaphore empty = new Semaphore(1);

    public void offer(T value) throws InterruptedException {
      empty.acquire();
      this.value = value;
      full.release();
    }

    public T take() throws InterruptedException {
      full.acquire();
      T returnValue = value;
      value = null; // Should release reference
      empty.release();
      return returnValue;
    }
  }

答案 2 :(得分:0)

请注意,由于RxJava-JDBC框架中ResultSet调用的时间安排,next()跳过了一些问题。我使用此实现修改先前给出的答案。

public final class SingleBlockingQueue<T> {
    private volatile T value;
    private final Semaphore nextGate = new Semaphore(0);
    private final Semaphore waitGate = new Semaphore(0);

    private volatile boolean hasValue = true;
    private volatile boolean isFirst = true;

    public void offer(T value) throws InterruptedException {
        if (isFirst) {
            nextGate.acquire();
            isFirst = false;
        }
        this.value = value;
        waitGate.release();
        nextGate.acquire();
    }

    public T take() throws InterruptedException {
        T returnValue = value;
        value = null; // Should release reference
        return returnValue;
    }
    public boolean next() throws InterruptedException {
        nextGate.release();
        waitGate.acquire();
        return hasValue;
    }
    public void setDone() {
        hasValue = false;
        waitGate.release();
    }
}

我正在使用它:将RxJava Observable<T>变成Kotlin中的Sequence<T>

import com.github.davidmoten.rx.jdbc.QuerySelect
import rx.Observable
import rx.Scheduler
import rx.lang.kotlin.subscribeWith
import java.io.Closeable

class ObservableIterator<T>(
        observable: Observable<T>
) : Iterator<T>, Closeable {

    private val queue = SingleBlockingQueue<T>()

    private val subscription =
            observable
                    .subscribeWith {
                        onNext { queue.offer(it) }
                        onCompleted { queue.setDone() }
                        onError { queue.setDone() }
                    }

    override fun hasNext(): Boolean {
        return queue.next()
    }

    override fun next(): T {
        return queue.take()
    }
    override fun close() {
        subscription.unsubscribe()
        queue.setDone()
    }
}

fun <T> Observable<T>.asSequence() = ObservableIterator(this).asSequence()

fun QuerySelect.Builder.asSequence(scheduler: Scheduler) = get { it }
        .subscribeOn(scheduler)
        .asSequence()