本人博客已经迁移至 www.shangyang.me 欢迎大家访问
1. Introduction
为了不让系统overloaded, 通常你不但需要创建线程,同时你也需要控制你的线程数量.
2. 方案
总体的规划是, 线程执行的过程中不超过一定临界值_latch。 如果超过了这个数量,那么会严重影响我的系统系能,甚至crash.
2.1 方案1 - 利用 CountDownLatch
CountDownLatch 的特性是,定义一个Latch值, 创建你的Threads, 如果你的数量不够这个 Latch值,CoundDownLatch 不会自动打开,执行你的线程. 一旦你的Threads 达到了 Latch 值,那么这些线程会一窝蜂的同时执行 ( 如果要测试 并发性,这个是个不错的选择 )。
该方案可以很好的控制我的线程数量在一个数量级,但是,这个并不是我的最优方案.
1. 线程 在达到 Latch 之前,都是阻塞住的。并不能让线程一直执行。
我的期望是,Threads可以自由的创建并执行,"只是当前同步线程达到 了_latch时,不允许新的线程继续创建,必须等待正在执行的线程结束以后,当前线程数量小于该临界值_latch后,允许新的线程创建并且执行"。
2.2 方案2 - 同步的线程不能超过临界值latch,但只要低于该latch, 线程都可以只有的创建和执行.
这种情况下,CountDownLatch 不能完全满足我们的要求,我们只能定义自己的 ThreadMonitor.
设计时序图:
import java.util.concurrent.CountDownLatch;
import org.junit.Test;
/**
*
* Prototype to monitor the concurrence threads.
*
* Design:
* Key:
* _latch: to limit the number of the current threads that running concurrence.
*
* Scenario:
* 1. If the current threads number not exceeds the _latch, initialize the threads and let them to start immediately.
* 2. If the current threads number equals the _latch, all the other threads need to wait.
* So this will guarantee that our system would be always less or equal than the _latch number of current threads executing at the same time to
* efficiently control our system not overloaded.
*
* @see java.util.concurrence.CountDownLatch to see how control the Latch.
*
*
* @author shangyang
*
*/
public class ThreadControlTest {
@Test
public void threadControlTest() throws InterruptedException{
ThreadMonitor monitor = new ThreadMonitor();
for(int i=0; i<100; i++){
MockThread thread = new MockThread(monitor);
// Increase the static _currentThreadNum of the ThreadMonitor .
monitor.increase();
thread.start();
/*
* If the _currentThreadNum exceeds/equals the _latch, main thread should be hanging on until the _currentThreadNum reduce to
* the num lower than _latch. So this would guarantee that there would be always no more than _latch threads concurrence.
*/
monitor.control();
System.out.println("i=" + i);
}
}
}
/**
*
* The monitor class to maintain the concurrence threads.
*
* @author shang yang
*
*/
class ThreadMonitor{
public static int _currentConcurThreadsNum = 0; // represents how many threads running currently.
final static int _latch = 5;
boolean _isMainThreadAwaited = false;
CountDownLatch _downLatch = null;
/**
* If the _currentThreadsNum exceeds the _latch, hanging on the current main thread. ( @see java.util.concurrence.CountDownLatch )
* It can not be made as synchronized, because it was used to hang on the main thread. see the scenarios below,
* If we added the synchronized, what the consequence it is:
* 1. The main thread is awaiting in controller() method. see _downLatch.await()
* 1.1 Because the controller() method is synchronized, and main thread owned the lock of the Object "ThreadMonitor"
* 1.2 That means, it will also locked the synchronized method increase() and countDown(),
* which means the other threads are also can not access the 2 methods
* 2. If #1 happened, we need the thread to execute the countDown(), and open the Latch to wake up the main thread
* But of the reason of #1.1 and #1.2, the lock of the Object "ThreadMonitor" has been owned by the main thread and can not release it
* So the thread can not invoke the ThreadMonitor.countDown() and always be waiting the main thread to release the Object lock.
*
* Finally, we will see that the main process is hanging on always cause
* 1. Main Thread waiting for the thread to countDown() to open the latch
* 2. The Thread waiting for the main Thread to release the lock on the control() method.
* --> The two are always waiting with each other all the time.
*
*
*
* @throws InterruptedException
*/
public void control() throws InterruptedException{
/*
*
*/
if( _currentConcurThreadsNum >= _latch ){
_downLatch = new CountDownLatch(1);
System.err.println("_currentConcurThreadsNum:"+_currentConcurThreadsNum+"; Exceeds the _latch, main thread hanging on, not allow any more threads initialization");
_isMainThreadAwaited = true;
_downLatch.await(); // let the main thread to wait.
}
}
/**
* if a new threads initialized, increase it.
*
* synchronized added here for the reason that _currentConcurThreadsNum is concurrence access.
*/
public synchronized void increase(){
_currentConcurThreadsNum ++;
System.out.println("_currentConcurThreadsNum is increased to:" + _currentConcurThreadsNum);
}
/**
* If a thread completed, count down the _currentThreadsNum. and more, if the _currentThreadsNum < _latch, we should open the _latch and let the main thread
* to continuous processing.
*/
public synchronized void countDown(){
_currentConcurThreadsNum --;
System.out.println("_currentConcurThreadsNum is reduced to:" + _currentConcurThreadsNum);
if ( _currentConcurThreadsNum < _latch ){
if(_isMainThreadAwaited){
_downLatch.countDown();
_isMainThreadAwaited = false;
System.err.println("now the _currentConcurThreadsNum:"+_currentConcurThreadsNum+"; Down the _latch, Latch open, allow "+ ( _latch-_currentConcurThreadsNum ) + " threads initialization ");
}
}
}
}
class MockThread extends Thread{
ThreadMonitor _monitor;
public MockThread(ThreadMonitor monitor){
_monitor = monitor;
}
@Override
public void run() {
// TODO Auto-generated method stub
// waiting here and reduce 1 for the COUNT_LATCH num.
// Until COUNT_LATCH has reduce to 0, continue.
System.out.println("Thread running~~~");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
_monitor.countDown();
System.out.println("Thread ended~~");
}
}