温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Tomcat如何进行并发编程

发布时间:2021-08-25 16:43:01 来源:亿速云 阅读:155 作者:chen 栏目:大数据

这篇文章主要讲解了“Tomcat如何进行并发编程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Tomcat如何进行并发编程”吧!

首先是最常用的synchronized

在容器的启动流程中,会从Server开始一直向到各个下层容器进行启动,下面的代码是Server找到配置的Service,进行遍历启动

private final Object servicesLock = new Object();

// Start our defined Services

       synchronized (servicesLock) {

           for (int i = 0; i < services.length; i++) {

               services[i].start();

           }

       }

其次,是锁范围的减小

service的启动过程,其实是这样的

 protected void startInternal() throws LifecycleException {

               // Start our defined Container first

               if (container != null) {

                   synchronized (container) {

                       container.start();

                   }

               }

               synchronized (executors) {

                   for (Executor executor: executors) {

                       executor.start();

                   }

               }

           }

上面的代码,我们看到,并不是整个方法进行加锁,而是对于各个容器内组件的启动进行分别加锁。这种对于锁作用范围和持有时间的缩小,可以降低锁竞争,提升可伸缩性。当然,如果说所有的内容都分别加锁反而会影响性能。感兴趣的朋友可以阅读Java并发编程实战的性能与可伸缩性一章,了解更多内容。

线程池启动容器内组件

// Start our child containers, if any

Container children[] = findChildren();

List<Future<Void>> results = new ArrayList<>();

for (int i = 0; i < children.length; i++) {

    results.add(startStopExecutor.submit(new StartChild(children[i])));

}


boolean fail = false;

for (Future<Void> result : results) {

    try {

        result.get();

    } catch (Exception e) {

 }}

各个容器获取到其子组件后,将其组装成一个任务,提交到任务执行线程池中。任务的执行结果,在通过其Future对象获取。

通过Callable封装带返回值的任务

private static class StartChild implements Callable<Void> {

        private Container child;

        public StartChild(Container child) {

            this.child = child;

        }

        public Void call() throws LifecycleException {

            child.start();

            return null;

   } }

由于组件的启动并不需要返回值,此处使用Void类型,可以在实际使用过程中换成具体的值返回具体的结果。在全部任务执行完成后,从Future中get返回值。

Volatile的使用

  private volatile boolean close = false;

  // Time to terminate?

       if (close) {

          timeout(0, false);

       try {

            selector.close();

       } catch (IOException ioe) {

  }

通过使用volatile值,来保证多线程环境中关闭标识的可见性,从而能正确的在标识改变后退出特定逻辑。

wait/notify的使用

在前面的概念中,我们提到使用wait/notify的时候,一定要在拿到锁的情况下进行。Tomcat在进行Servlet实例allocate和deallocate的时候,会使用到这两个操作。

      synchronized (instancePool)

         while (countAllocated.get() >= nInstances) {

             if (nInstances < maxInstances) {

                 instancePool.push(loadServlet());

                 nInstances++;

             } else {

                 instancePool.wait();

             }

   }

卸载的时候,代码是这样的

         synchronized (instancePool) {

             countAllocated.decrementAndGet();

             instancePool.push(servlet);

             instancePool.notify();

         }

两种情况都是先拿到锁再进行的。

当然,Tomcat中也有许多对JDK并发包内组件的使用,像下面这个对于CountDownLatch的使用

private volatile CountDownLatch stopLatch = null; 

 stopLatch = new CountDownLatch(pollerThreadCount); // 在Endpoint进行bind操作时,设置相应poller数量的CountDownLatch


// 在处理destory时,进行countDown操作,后续的关闭操作,会根据stopLatch的数据进行等待操作。

stopLatch.countDown();

感谢各位的阅读,以上就是“Tomcat如何进行并发编程”的内容了,经过本文的学习后,相信大家对Tomcat如何进行并发编程这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI