最初の2つは非常に不完全な輻輳キューであり、第三は、より完璧とみなされ、直接、第三ブロックされたキューを見ることができます。
まず、すべての混雑キュー:
locksupportを使用し、全体のプロセス、および操作の独自のリストを待ってから通知を実装します。
短所:入れて操作を取得し、ロックが共有されている実際には、取得することができますし、並列に入れ、put操作キューヘッドので、尾は、それが並列動作で取得し、ため、最適化の余地があります。
public class LinkedQueue<T> {
// 维护一个数据节点双向链表的head和tail指针
private Node tail;
private Node head;
// 维护一个get线程节点双向链表的head和tail指针
private ThreadNode getHeadThread;
private ThreadNode getTailThread;
// 维护一个put线程节点双向链表的head和tail指针
private ThreadNode putHeadThread;
private ThreadNode putTailThread;
private Integer size;
private Integer count;
// put锁
ReentrantLock lock = new ReentrantLock();
public LinkedQueue(Integer size) {
this.size = size;
this.count = 0;
this.tail = new Node();
this.head = new Node();
head.next = tail;
tail.prev = head;
getHeadThread = new ThreadNode();
getTailThread = new ThreadNode();
getHeadThread.next = getTailThread;
getTailThread.prev = getHeadThread;
putHeadThread = new ThreadNode();
putTailThread = new ThreadNode();
putHeadThread.next = putTailThread;
putTailThread.prev = putHeadThread;
}
// 存储元素的
class Node {
public T item;
public Node next;
public Node prev;
public Node(T item, Node next, Node prev) {
this.item = item;
this.next = next;
this.prev = prev;
}
public Node() {}
}
class ThreadNode {
public Thread thread;
public ThreadNode next;
public ThreadNode prev;
public ThreadNode() {}
public ThreadNode(Thread thread, ThreadNode next, ThreadNode prev) {
this.thread = thread;
this.next = next;
this.prev = prev;
}
}
public void put(T item) {
lock.lock();
// 容量已经满了
if (size.equals(count)) {
Thread thread = Thread.currentThread();
ThreadNode temp = putHeadThread.next;
ThreadNode threadNode = new ThreadNode(thread, temp, putHeadThread);
temp.prev = threadNode;
putHeadThread.next = threadNode;
lock.unlock();
LockSupport.park();
this.put(item);
return;
}
Node temp = head.next;
// 把node加入head的下一个节点,维护一个双链表结构
Node node = new Node(item, temp, head);
head.next = node;
temp.prev = node;
count++;
if (getTailThread.prev != getHeadThread) {
ThreadNode prev = getTailThread.prev;
ThreadNode t = prev.prev;
getTailThread.prev = t;
t.next = getTailThread;
Thread thread = prev.thread;
LockSupport.unpark(thread);
}
lock.unlock();
}
public T get() {
lock.lock();
Node prev = tail.prev;
if (prev == head) {
// 队列为空,堵塞
Thread thread = Thread.currentThread();
ThreadNode temp = getHeadThread.next;
ThreadNode threadNode = new ThreadNode(thread, temp, getHeadThread);
temp.prev = threadNode;
getHeadThread.next = threadNode;
lock.unlock();
LockSupport.park();
return this.get();
}
Node temp = prev.prev;
tail.prev = temp;
temp.next = tail;
count--;
// 唤醒put的线程
if (putTailThread.prev != putHeadThread) {
ThreadNode prevPut = putTailThread.prev;
ThreadNode t = prevPut.prev;
putTailThread.prev = t;
t.next = putTailThread;
Thread thread = prevPut.thread;
LockSupport.unpark(thread);
}
lock.unlock();
return prev.item;
}
}
テストカテゴリ:それはgetおよびput同期シリアルですので、ノー並行性の問題、何の問題をテストしていないのは簡単です
public class Provider {
LinkedQueue<Integer> linkedQueue = new LinkedQueue<>(3);
AtomicInteger putNum = new AtomicInteger();
long start;
public void put() {
for (int i = 0; i < 1000; i++) {
int andIncrement = putNum.getAndIncrement();
linkedQueue.put(andIncrement);
System.out.println("put in " + Thread.currentThread().getName() + " " + andIncrement
+ " endTime:" + (System.currentTimeMillis() - start));
}
}
public void get() {
while (true) {
Integer integer = linkedQueue.get();
System.out.println(Thread.currentThread().getName() + " " + integer
+ " endTime:" + (System.currentTimeMillis() - start));
}
}
public static void main(String[] args) throws InterruptedException {
Provider provider = new Provider();
provider.start = System.currentTimeMillis();
for (int i = 0; i < 50; i++) {
new Thread(() -> {
provider.put();
}).start();
}
System.out.println("get");
for (int i = 0; i < 50; i++) {
new Thread(() -> provider.get()).start();
}
// Thread.sleep(5000);
// System.out.println("ok");
// provider.put();
}
}
二混雑キュー:
上記とlocksupportリストの代わりに条件。
それは非常にきれいに見えますが、実際には非効率的。
短所:1以上、同期キューです。
2.signalはsignallで、毎回のウェイクアップすべての待機をした後、スレッドは、待ち時間を入力し、無駄な作業の多くを行う、私たちはされて目を覚ますまで待って、リストをlinkedblockする条件リスト、待ちリストの状態になりますlinkedblockですリストは、この役に立たない多くの作業を行います。
public class LinkedQueue2<T> {
// 维护一个数据节点双向链表的head和tail指针
private Node tail;
private Node head;
private Integer size;
private Integer count;
// put锁
ReentrantLock lock = new ReentrantLock();
Condition getCondition = lock.newCondition();
Condition putCondition = lock.newCondition();
public LinkedQueue2(Integer size) {
this.size = size;
this.count = 0;
this.tail = new LinkedQueue2.Node();
this.head = new LinkedQueue2.Node();
head.next = tail;
tail.prev = head;
}
// 存储元素的
class Node {
public T item;
public LinkedQueue2.Node next;
public LinkedQueue2.Node prev;
public Node(T item, LinkedQueue2.Node next, LinkedQueue2.Node prev) {
this.item = item;
this.next = next;
this.prev = prev;
}
public Node() {}
}
public void put(T item) throws InterruptedException {
lock.lock();
// 容量已经满了
if (size.equals(count)) {
putCondition.await();
}
LinkedQueue2.Node temp = head.next;
// 把node加入head的下一个节点,维护一个双链表结构
LinkedQueue2.Node node = new LinkedQueue2.Node(item, temp, head);
head.next = node;
temp.prev = node;
count++;
if (count == 0) {
getCondition.signalAll();
}
lock.unlock();
}
public T get() throws InterruptedException {
lock.lock();
// 队列为空,堵塞
if (size == 0) {
getCondition.await();
}
Node temp = tail.prev;
tail.prev = temp;
temp.next = tail;
// 唤醒put的线程
if (size.equals(count)) {
putCondition.signalAll();
}
count--;
lock.unlock();
return temp.item;
}
}
テスト
public class LinkedQueue2<T> {
// 维护一个数据节点双向链表的head和tail指针
private Node tail;
private Node head;
private Integer size;
private Integer count;
// put锁
ReentrantLock lock = new ReentrantLock();
Condition getCondition = lock.newCondition();
Condition putCondition = lock.newCondition();
public LinkedQueue2(Integer size) {
this.size = size;
this.count = 0;
this.tail = new LinkedQueue2.Node();
this.head = new LinkedQueue2.Node();
head.next = tail;
tail.prev = head;
}
// 存储元素的
class Node {
public T item;
public LinkedQueue2.Node next;
public LinkedQueue2.Node prev;
public Node(T item, LinkedQueue2.Node next, LinkedQueue2.Node prev) {
this.item = item;
this.next = next;
this.prev = prev;
}
public Node() {}
}
public void put(T item) throws InterruptedException {
lock.lock();
// 容量已经满了
if (size.equals(count)) {
putCondition.await();
}
LinkedQueue2.Node temp = head.next;
// 把node加入head的下一个节点,维护一个双链表结构
LinkedQueue2.Node node = new LinkedQueue2.Node(item, temp, head);
head.next = node;
temp.prev = node;
count++;
if (count == 0) {
getCondition.signalAll();
}
lock.unlock();
}
public T get() throws InterruptedException {
lock.lock();
// 队列为空,堵塞
if (size == 0) {
getCondition.await();
}
Node temp = tail.prev;
tail.prev = temp;
temp.next = tail;
// 唤醒put的线程
if (size.equals(count)) {
putCondition.signalAll();
}
count--;
lock.unlock();
return temp.item;
}
}
第三混雑キュー:
ソースコードLinkedBlockQueue政策的含意。
1、問題を解決し、同時実行を入れてもらうため、取得し、何の並行性の問題を入れないように、単一リンクリストを使用して。取得と並行して置くようにし、取得し、2つのロックを置きます。
2ので、自分でウェイクアップスレッドをゲットするには、自分のウェイクアップを取得し、シリアライズ、まれロック競争を入れて糸を入れて置きます。
、whileループを使用してそのような待機操作など多くの詳細は、待ち時間を短縮し、スレッドに頻繁な切り替えを通知し、余分な通話や他の同時実行の問題を通知するために注意を払う512回スピン、単鎖が頭から取得します、3があり、詳細はコードのコメントを参照してください。
public class LinkedQueue3<T> {
ReentrantLock putLock = new ReentrantLock();
ReentrantLock getLock = new ReentrantLock();
private Node tail;
private Node head;
private final Integer CAPACITY;
private AtomicInteger size;
Condition putCondition = putLock.newCondition();
Condition getCondition = getLock.newCondition();
// 存储元素的
class Node {
public T item;
public Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
public Node() {}
}
public LinkedQueue3(Integer capacity) {
CAPACITY = capacity;
this.size = new AtomicInteger();
this.head = this.tail = new Node();
}
public void put(T item) throws InterruptedException {
putLock.lock();
// 容量已经满了
// 这里为什么要while?因为被唤醒,不一定就立即获得锁
// 如果被唤醒没有获得锁,被别的put线程获得锁了,然后
// 马上添加了一个元素,则被唤醒的线程,获得锁之后,则
// 容量已经满了,所以需要while继续等待
// 所以对于多线程wait操作,一定不要忘了wait操作用while循环做
while (size.get() == CAPACITY) {
/*
如果在这里唤醒,就会出问题,因为,容量满了之后第一个put操作,
唤醒了一个get线程,那个get线程还没有get元素,则又一个put操作,又
会唤醒一个get线程,则如此,会唤醒很多个get线程,然后很多个get线程去
争取getlock,一个get线程争取到了,size-1,又唤醒了一个get线程,
因为size=capacity,又唤醒了一个put线程,put操作还没来得及put,
get操作已经get完了,
最后,size为0,则所有get操作堵塞,而且唤醒了多个put操作,
也就是说,最后,put线程和get线程都醒了,
signal就没有用了,然后signal后await,最后都堵塞了,没人唤醒了
//对我们的启示是,signal操作,不要有多个地方都signal,一个地方
就够了,多个地方,很容易出几个回合之后,唤醒多个线程的并发问题
*/
// 唤醒get
// getLock.lock();
// getCondition.signal();
// getLock.unlock();
// 当想进行await操作的时候,先自旋512次,减少,高并发下频繁await和notify的线程切换,设计思想来源于sequenceQueue源代码
for(int i = 0; i < 512; i++){
if(size.get() != CAPACITY){
break;
}else{
if(i == 512){
putCondition.await();
}
}
}
}
// 入队
tail = tail.next = new Node(item, null);
int beforeSize = size.getAndIncrement();
if(beforeSize + 1 < CAPACITY){
putCondition.signal();
}
putLock.unlock();
// 如果入队前,队列为null,则唤醒get
if(beforeSize == 0){
getLock.lock();
getCondition.signal();
getLock.unlock();
}
}
public T get() throws InterruptedException {
getLock.lock();
// 队列为空,堵塞
while (size.get() == 0) {
// 唤醒put操作
// putLock.lock();
// putCondition.signal();
// putLock.unlock();
getCondition.await();
}
// 出队
Node h = head;
Node first = head.next;
h.next = h;
head = first;
T value = head.item;
head.item = null;
// 只能移除head元素,然后把head的下一个节点变成head,而不是,
// 这样,直接移除head的下一个元素,否则head的下一个元素可能为tail,不能把tail移除了,而且还有其他问题
// Node node = head.next;
// head.next = node.next;
// node.next = null;
// T value = node.item;
int beforeSize = size.getAndDecrement();
// 还有元素
if(beforeSize - 1 != 0){
getCondition.signal();
}
getLock.unlock();
// 出队之前,容量是满的
if (beforeSize == CAPACITY) {
putLock.lock();
putCondition.signal();
putLock.unlock();
}
return value;
}
}
テストコード:
public class Provider3 {
LinkedQueue3<Integer> linkedQueue = new LinkedQueue3<>(3);
AtomicInteger putNum = new AtomicInteger();
long start;
public void put() throws InterruptedException {
for (int i = 0; i < 100; i++) {
int andIncrement = putNum.getAndIncrement();
linkedQueue.put(andIncrement);
System.out.println("put in " + Thread.currentThread().getName() + " " + andIncrement
+ " endTime:" + (System.currentTimeMillis() - start));
}
}
public void get() throws InterruptedException {
while (true) {
Integer integer = linkedQueue.get();
System.out.println(Thread.currentThread().getName() + " " + integer
+ " endTime:" + (System.currentTimeMillis() - start));
}
}
public static void main(String[] args) throws InterruptedException {
Provider3 provider = new Provider3();
provider.start = System.currentTimeMillis();
for (int i = 0; i < 50; i++) {
new Thread(() -> {
try {
provider.put();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
System.out.println("get");
for (int i = 0; i < 50; i++) {
new Thread(() -> {
try {
provider.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// Thread.sleep(5000);
// System.out.println("ok");
// provider.put();
}
}
ます。https://www.jianshu.com/p/a6a864c9f877で再現