线程池的应用-ChromeDriver自动化引发的思考
1.前言
在ChromeDriver自动化开发过程中,左耳碰到了一个问题,就是ChromeDriver驱动是比较消耗资源的存在,所以要对它进行管制,正好左耳了解线程池的机制,其作用就是节约资源,避免不必要的创建与销毁开销,而ChromeDriver不也是如此么。
2.设计与实现
流程分析
2.1 数据初始化
2.2 getPool根据coreSize判断是否新增资源,其中count要使用原子自增
2.3 Worker的status要设置为volatile,让其他线程可见
public class ChromeDriverPool {
private static ConcurrentHashMap<Integer,Object> pool ;//线程池
private static int coreSize = 2;核心线程池,一直驻留的资源
private static int maxSize = 10;//TODO 设置最大数量资源,用于解决瞬时并发,瞬时并发过后要定时去识别空闲的释放掉,保留coreSize的资源
private static AtomicInteger count = new AtomicInteger(0);//原子自增!!
static {
//驱动初始化
System.setProperty("webdriver.chrome.driver", "/usr/local/bin/chromedriver");
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
System.setProperty("webdriver.chrome.driver", "C:\\usr\\webser\\chromedriver.exe");
}
pool = new ConcurrentHashMap<>();
}
private ChromeDriverPool(){
}
public static void init(int coreSize){//线程池数量初始化
ChromeDriverPool.coreSize = coreSize;
}
public static Worker getPool(){
if(count.getAndIncrement() < coreSize){//自增!!!
return push(new Worker());
}else{
return pop();
}
}
public static Worker push(Worker worker){
pool.put(count.get(),worker);
return worker;
}
/**
* 循环获取未使用的资源
* @return
*/
public static Worker pop(){
Set<Map.Entry<Integer, Object>> sets = pool.entrySet();
Iterator<Map.Entry<Integer, Object>> its = sets.iterator();
while(its.hasNext()){
Map.Entry<Integer, Object> workEntry = its.next();
Worker work = (Worker)workEntry.getValue();
if(work.status == 1){
return work;
}
}
return null;
}
public static class Worker{
private ChromeDriver driver;
/**
* 状态修改要让其他线程可见
* 1就绪 2运行
*/
private volatile int status;
public Worker(){
driver = new ChromeDriver();
status = 1;
}
public void shutdown(){
this.status = 1;
}
public void start(){
this.status = 2;
}
public ChromeDriver getDriver() {
return driver;
}
}
}
2.4 模拟并发环境测试
public static void main(String[] args) throws Exception {
//模拟并发
int parallelSize = 10;
final CountDownLatch latch = new CountDownLatch(1);
ExecutorService pool = Executors.newFixedThreadPool(parallelSize);
for (int i = 0; i < parallelSize; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
ChromeDriverPool.Worker work = null;
try {
latch.await();//阻塞所有线程
System.out.println(Thread.currentThread().getName());
while (true) {//业务需求,一直等待直到获取到驱动
work = ChromeDriverPool.getPool();
if (work != null) {
break;
}
Thread.sleep(1000);
}
work.start();
ChromeDriver driver = work.getDriver();
System.out.println(Thread.currentThread().getName()+":"+driver);
Thread.sleep(1000 * 10);
} catch (Exception e) {
System.out.println(e.getMessage());
}finally {
work.shutdown();
}
}
});
}
latch.countDown();//开炮
}
3.总结
到此一个简单的线程池应用就完成了,让资源重复利用,节省开销,那么有没有要优化的点呢?
我们知道ThreadPoolExecutor有几个核心参数(更多ThreadPoolExecutor知识请参考 链接)
corePoolSize
为线程池的基本大小。maximumPoolSize
为线程池最大线程大小。keepAliveTime
和unit
则是线程空闲后的存活时间。workQueue
用于存放任务的阻塞队列。handler
当队列和最大线程池都满了之后的饱和策略。
我们可以按此去优化我们的程序,比如maximumPoolSize的应用
,可以解决瞬时并发的问题。到此我们就结束这一篇章,有兴趣的朋友可以尝试实现一下maximumPoolSize。