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~~"); } }