Modified BlockingBuffer add method to notifyAll
instead of notify. Added tests to verify blocking behavior. Patch submitted by: Janek Bogucki Reviewed by: Phil Steitz Pr #23232, 23159 git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131161 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c3992caf1d
commit
9f3a1ecf83
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/decorators/Attic/BlockingBuffer.java,v 1.2 2003/08/31 17:24:46 scolebourne Exp $
|
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/decorators/Attic/BlockingBuffer.java,v 1.3 2003/09/18 03:28:28 psteitz Exp $
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
*
|
*
|
||||||
* The Apache Software License, Version 1.1
|
* The Apache Software License, Version 1.1
|
||||||
|
@ -64,13 +64,14 @@ import org.apache.commons.collections.BufferUnderflowException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>BlockingBuffer</code> decorates another <code>Buffer</code>
|
* <code>BlockingBuffer</code> decorates another <code>Buffer</code>
|
||||||
* to block on calls to the get method to wait until entries are
|
* to block on calls to the get and remove methods to wait until entries are
|
||||||
* added to the buffer.
|
* added to the buffer.
|
||||||
*
|
*
|
||||||
* @since Commons Collections 3.0
|
* @since Commons Collections 3.0
|
||||||
* @version $Revision: 1.2 $ $Date: 2003/08/31 17:24:46 $
|
* @version $Revision: 1.3 $ $Date: 2003/09/18 03:28:28 $
|
||||||
*
|
*
|
||||||
* @author Stephen Colebourne
|
* @author Stephen Colebourne
|
||||||
|
* @author Janek Bogucki
|
||||||
*/
|
*/
|
||||||
public class BlockingBuffer extends SynchronizedBuffer {
|
public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ public class BlockingBuffer extends SynchronizedBuffer {
|
||||||
public boolean add(Object o) {
|
public boolean add(Object o) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
boolean result = collection.add(o);
|
boolean result = collection.add(o);
|
||||||
notify();
|
notifyAll();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/decorators/Attic/TestBlockingBuffer.java,v 1.1 2003/09/15 03:50:41 psteitz Exp $
|
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/decorators/Attic/TestBlockingBuffer.java,v 1.2 2003/09/18 03:28:28 psteitz Exp $
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
*
|
*
|
||||||
* The Apache Software License, Version 1.1
|
* The Apache Software License, Version 1.1
|
||||||
|
@ -57,15 +57,16 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections.decorators;
|
package org.apache.commons.collections.decorators;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
import junit.framework.TestSuite;
|
import junit.framework.TestSuite;
|
||||||
|
|
||||||
import org.apache.commons.collections.Buffer;
|
import org.apache.commons.collections.Buffer;
|
||||||
import org.apache.commons.collections.ArrayStack;
|
|
||||||
import org.apache.commons.collections.BufferUnderflowException;
|
import org.apache.commons.collections.BufferUnderflowException;
|
||||||
import org.apache.commons.collections.decorators.BlockingBuffer;
|
import org.apache.commons.collections.decorators.BlockingBuffer;
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ import org.apache.commons.collections.TestObject;
|
||||||
* implementation.
|
* implementation.
|
||||||
*
|
*
|
||||||
* @since Commons Collections 3.0
|
* @since Commons Collections 3.0
|
||||||
* @version $Revision: 1.1 $
|
* @version $Revision: 1.2 $
|
||||||
*
|
*
|
||||||
* @author Janek Bogucki
|
* @author Janek Bogucki
|
||||||
* @author Phil Steitz
|
* @author Phil Steitz
|
||||||
|
@ -102,7 +103,7 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#get()}.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add()}.
|
||||||
*/
|
*/
|
||||||
public void testGetWithAdd() {
|
public void testGetWithAdd() {
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#get()}.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll()}.
|
||||||
*/
|
*/
|
||||||
public void testGetWithAddAll() {
|
public void testGetWithAddAll() {
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#remove()}.
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add()}.
|
||||||
*/
|
*/
|
||||||
public void testRemoveWithAdd() {
|
public void testRemoveWithAdd() {
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests {@link BlockingBuffer#remove()}.
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll()}.
|
||||||
*/
|
*/
|
||||||
public void testRemoveWithAddAll() {
|
public void testRemoveWithAddAll() {
|
||||||
|
|
||||||
|
@ -159,107 +160,74 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
// verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
|
||||||
assertSame(obj, blockingBuffer.remove());
|
assertSame(obj, blockingBuffer.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests get using multiple read threads.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add()} using multiple read threads.
|
||||||
*
|
*
|
||||||
* Verifies that multiple adds are required to allow gets by
|
* Two read threads should block on an empty buffer until one object
|
||||||
* multiple threads on an empty buffer to complete.
|
* is added then both threads should complete.
|
||||||
*/
|
*/
|
||||||
public void testBlockedGetWithAdd() {
|
public void testBlockedGetWithAdd() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
|
Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
// run methods will get and compare -- must wait for adds
|
// run methods will get and compare -- must wait for add
|
||||||
Thread thread1 = new ReadThread(blockingBuffer, obj);
|
Thread thread1 = new ReadThread(blockingBuffer, obj);
|
||||||
Thread thread2 = new ReadThread(blockingBuffer, obj);
|
Thread thread2 = new ReadThread(blockingBuffer, obj);
|
||||||
thread1.start();
|
thread1.start();
|
||||||
thread2.start();
|
thread2.start();
|
||||||
|
|
||||||
// give hungry read threads ample time to hang
|
// give hungry read threads ample time to hang
|
||||||
try {
|
delay();
|
||||||
Thread.currentThread().sleep(100);
|
|
||||||
} catch (InterruptedException e) {}
|
|
||||||
|
|
||||||
// notify should allow one read thread to complete
|
// notifyAll should allow both read threads to complete
|
||||||
blockingBuffer.add(obj);
|
blockingBuffer.add(obj);
|
||||||
|
|
||||||
// allow notified thread(s) to complete
|
// allow notified threads to complete
|
||||||
try {
|
delay();
|
||||||
Thread.currentThread().sleep(100);
|
|
||||||
} catch (InterruptedException e) {}
|
|
||||||
|
|
||||||
// There shoould still be one thread waiting. Verify this.
|
// There should not be any threads waiting.
|
||||||
// This check will fail if add is changed to notifyAll.
|
if (thread1.isAlive() || thread2.isAlive())
|
||||||
assertTrue("One read thread should be waiting",
|
fail("Live thread(s) when both should be dead.");
|
||||||
thread1.isAlive() || thread2.isAlive());
|
|
||||||
|
|
||||||
// now add again so the second thread will be notified
|
|
||||||
blockingBuffer.add(obj);
|
|
||||||
|
|
||||||
// wait to exit until both threads are dead, or appear to be hung
|
|
||||||
boolean finished = false;
|
|
||||||
for (int i = 1; i < 10; i++) {
|
|
||||||
if (thread1.isAlive() || thread2.isAlive()) {
|
|
||||||
try {
|
|
||||||
Thread.currentThread().sleep(100);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {}
|
|
||||||
} else {
|
|
||||||
finished = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!finished) {
|
|
||||||
fail("Read thread did not finish.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests get using multiple read threads.
|
* Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll()} using multiple read threads.
|
||||||
* Shows that one addAll allows multiple gets to complete.
|
*
|
||||||
|
* Two read threads should block on an empty buffer until a
|
||||||
|
* singleton is added then both threads should complete.
|
||||||
*/
|
*/
|
||||||
public void testBlockedGetWithAddAll() {
|
public void testBlockedGetWithAddAll() {
|
||||||
|
|
||||||
Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
|
Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
|
||||||
Object obj = new Object();
|
Object obj = new Object();
|
||||||
|
|
||||||
// run methods will get and compare -- must wait for adds
|
// run methods will get and compare -- must wait for addAll
|
||||||
Thread thread1 = new ReadThread(blockingBuffer, obj);
|
Thread thread1 = new ReadThread(blockingBuffer, obj);
|
||||||
Thread thread2 = new ReadThread(blockingBuffer, obj);
|
Thread thread2 = new ReadThread(blockingBuffer, obj);
|
||||||
thread1.start();
|
thread1.start();
|
||||||
thread2.start();
|
thread2.start();
|
||||||
|
|
||||||
// give hungry read threads ample time to hang
|
// give hungry read threads ample time to hang
|
||||||
try {
|
delay();
|
||||||
Thread.currentThread().sleep(100);
|
|
||||||
} catch (InterruptedException e) {}
|
|
||||||
|
|
||||||
// notifyAll should allow both read threads to complete
|
// notifyAll should allow both read threads to complete
|
||||||
blockingBuffer.addAll(Collections.singleton(obj));
|
blockingBuffer.addAll(Collections.singleton(obj));
|
||||||
|
|
||||||
// wait to exit until both threads are dead, or appear to be hung
|
// allow notified threads to complete
|
||||||
boolean finished = false;
|
delay();
|
||||||
for (int i = 1; i < 10; i++) {
|
|
||||||
if (thread1.isAlive() || thread2.isAlive()) {
|
// There should not be any threads waiting.
|
||||||
try {
|
if (thread1.isAlive() || thread2.isAlive())
|
||||||
Thread.currentThread().sleep(100);
|
fail("Live thread(s) when both should be dead.");
|
||||||
}
|
|
||||||
catch (InterruptedException e) {}
|
|
||||||
} else {
|
|
||||||
finished = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!finished) {
|
|
||||||
fail("Read thread did not finish.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests interrupted get.
|
* Tests interrupted {@link BlockingBuffer#get()}.
|
||||||
*/
|
*/
|
||||||
public void testInterruptedGet() {
|
public void testInterruptedGet() {
|
||||||
|
|
||||||
|
@ -275,19 +243,140 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
|
|
||||||
// Chill, so thread can throw and add message to exceptionList
|
// Chill, so thread can throw and add message to exceptionList
|
||||||
try {
|
delay();
|
||||||
Thread.currentThread().sleep(100);
|
|
||||||
} catch (InterruptedException e) {}
|
|
||||||
|
|
||||||
assertTrue("Thread interrupt should have led to underflow",
|
assertTrue("Thread interrupt should have led to underflow",
|
||||||
exceptionList.contains("BufferUnderFlow"));
|
exceptionList.contains("BufferUnderFlow"));
|
||||||
|
|
||||||
if (thread.isAlive()) {
|
if (thread.isAlive()) {
|
||||||
fail("Hung read thread");
|
fail("Read thread has hung.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add()} using multiple read threads.
|
||||||
|
*
|
||||||
|
* Two read threads should block on an empty buffer until one
|
||||||
|
* object is added then one thread should complete. The remaining
|
||||||
|
* thread should complete after the addition of a second object.
|
||||||
|
*/
|
||||||
|
public void testBlockedRemoveWithAdd() {
|
||||||
|
|
||||||
|
Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
|
||||||
|
Object obj = new Object();
|
||||||
|
|
||||||
|
// run methods will remove and compare -- must wait for add
|
||||||
|
Thread thread1 = new ReadThread(blockingBuffer, obj, null, "remove");
|
||||||
|
Thread thread2 = new ReadThread(blockingBuffer, obj, null, "remove");
|
||||||
|
thread1.start();
|
||||||
|
thread2.start();
|
||||||
|
|
||||||
|
// give hungry read threads ample time to hang
|
||||||
|
delay();
|
||||||
|
|
||||||
|
blockingBuffer.add(obj);
|
||||||
|
|
||||||
|
// allow notified threads to complete
|
||||||
|
delay();
|
||||||
|
|
||||||
|
// There should be one thread waiting.
|
||||||
|
assertTrue ("There is one thread waiting", thread1.isAlive() ^ thread2.isAlive());
|
||||||
|
|
||||||
|
blockingBuffer.add(obj);
|
||||||
|
|
||||||
|
// allow notified thread to complete
|
||||||
|
delay();
|
||||||
|
|
||||||
|
// There should not be any threads waiting.
|
||||||
|
if(thread1.isAlive() || thread2.isAlive())
|
||||||
|
fail("Live thread(s) when both should be dead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll()} using multiple read threads.
|
||||||
|
*
|
||||||
|
* Two read threads should block on an empty buffer until a
|
||||||
|
* singleton collection is added then one thread should
|
||||||
|
* complete. The remaining thread should complete after the
|
||||||
|
* addition of a second singleton.
|
||||||
|
*/
|
||||||
|
public void testBlockedRemoveWithAddAll1() {
|
||||||
|
|
||||||
|
Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
|
||||||
|
Object obj = new Object();
|
||||||
|
|
||||||
|
// run methods will remove and compare -- must wait for addAll
|
||||||
|
Thread thread1 = new ReadThread(blockingBuffer, obj, null, "remove");
|
||||||
|
Thread thread2 = new ReadThread(blockingBuffer, obj, null, "remove");
|
||||||
|
thread1.start();
|
||||||
|
thread2.start();
|
||||||
|
|
||||||
|
// give hungry read threads ample time to hang
|
||||||
|
delay();
|
||||||
|
|
||||||
|
blockingBuffer.addAll(Collections.singleton(obj));
|
||||||
|
|
||||||
|
// allow notified threads to complete
|
||||||
|
delay();
|
||||||
|
|
||||||
|
// There should be one thread waiting.
|
||||||
|
assertTrue ("There is one thread waiting", thread1.isAlive() ^ thread2.isAlive());
|
||||||
|
|
||||||
|
blockingBuffer.addAll(Collections.singleton(obj));
|
||||||
|
|
||||||
|
// allow notified thread to complete
|
||||||
|
delay();
|
||||||
|
|
||||||
|
// There should not be any threads waiting.
|
||||||
|
if(thread1.isAlive() || thread2.isAlive())
|
||||||
|
fail("Live thread(s) when both should be dead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll()} using multiple read threads.
|
||||||
|
*
|
||||||
|
* Two read threads should block on an empty buffer until a
|
||||||
|
* collection with two distinct objects is added then both
|
||||||
|
* threads should complete. Each thread should have read a
|
||||||
|
* different object.
|
||||||
|
*/
|
||||||
|
public void testBlockedRemoveWithAddAll2() {
|
||||||
|
|
||||||
|
Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
|
||||||
|
Object obj1 = new Object();
|
||||||
|
Object obj2 = new Object();
|
||||||
|
|
||||||
|
Set objs = Collections.synchronizedSet(new HashSet());
|
||||||
|
objs.add(obj1);
|
||||||
|
objs.add(obj2);
|
||||||
|
|
||||||
|
// run methods will remove and compare -- must wait for addAll
|
||||||
|
Thread thread1 = new ReadThread(blockingBuffer, objs, "remove");
|
||||||
|
Thread thread2 = new ReadThread(blockingBuffer, objs, "remove");
|
||||||
|
thread1.start();
|
||||||
|
thread2.start();
|
||||||
|
|
||||||
|
// give hungry read threads ample time to hang
|
||||||
|
delay();
|
||||||
|
|
||||||
|
blockingBuffer.addAll(objs);
|
||||||
|
|
||||||
|
// allow notified threads to complete
|
||||||
|
delay();
|
||||||
|
|
||||||
|
assertEquals("Both objects were removed", 0, objs.size());
|
||||||
|
|
||||||
|
// There should not be any threads waiting.
|
||||||
|
if(thread1.isAlive() || thread2.isAlive())
|
||||||
|
fail("Live thread(s) when both should be dead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Tests interrupted remove.
|
* Tests interrupted remove.
|
||||||
*/
|
*/
|
||||||
|
@ -305,15 +394,13 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
|
|
||||||
// Chill, so thread can throw and add message to exceptionList
|
// Chill, so thread can throw and add message to exceptionList
|
||||||
try {
|
delay();
|
||||||
Thread.currentThread().sleep(100);
|
|
||||||
} catch (InterruptedException e) {}
|
|
||||||
|
|
||||||
assertTrue("Thread interrupt should have led to underflow",
|
assertTrue("Thread interrupt should have led to underflow",
|
||||||
exceptionList.contains("BufferUnderFlow"));
|
exceptionList.contains("BufferUnderFlow"));
|
||||||
|
|
||||||
if (thread.isAlive()) {
|
if (thread.isAlive()) {
|
||||||
fail("Hung read thread");
|
fail("Read thread has hung.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -370,6 +457,7 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
Object obj;
|
Object obj;
|
||||||
ArrayList exceptionList = null;
|
ArrayList exceptionList = null;
|
||||||
String action = "get";
|
String action = "get";
|
||||||
|
Set objs;
|
||||||
|
|
||||||
ReadThread (Buffer buffer, Object obj) {
|
ReadThread (Buffer buffer, Object obj) {
|
||||||
super();
|
super();
|
||||||
|
@ -392,12 +480,22 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReadThread (Buffer buffer, Set objs, String action) {
|
||||||
|
super();
|
||||||
|
this.buffer = buffer;
|
||||||
|
this.objs = objs;
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
if (action == "get") {
|
if (action == "get") {
|
||||||
assertSame(obj, buffer.get());
|
assertSame(obj, buffer.get());
|
||||||
} else {
|
} else {
|
||||||
assertSame(obj, buffer.remove());
|
if (null != obj)
|
||||||
|
assertSame(obj, buffer.remove());
|
||||||
|
else
|
||||||
|
assertTrue(objs.remove(buffer.remove()));
|
||||||
}
|
}
|
||||||
} catch (BufferUnderflowException ex) {
|
} catch (BufferUnderflowException ex) {
|
||||||
exceptionList.add("BufferUnderFlow");
|
exceptionList.add("BufferUnderFlow");
|
||||||
|
@ -420,4 +518,10 @@ public class TestBlockingBuffer extends TestObject {
|
||||||
return remove(0);
|
return remove(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void delay(){
|
||||||
|
try {
|
||||||
|
Thread.currentThread().sleep(100);
|
||||||
|
} catch (InterruptedException e) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue