JDK1.7 FutureTask

功能简介

  • FutureTask是一种异步任务(或异步计算),举个栗子,主线程的逻辑中需要使用某个值,但这个值需要负责的运算得来,那么主线程可以提前建立一个异步任务来计算这个值(在其他的线程中计算),然后去做其他事情,当需要这个值的时候再通过刚才建立的异步任务来获取这个值,有点并行的意思,这样可以缩短整个主线程逻辑的执行时间。
  • 1.7与1.6版本不同,1.7的FutureTask不再基于AQS来构建,而是在内部采用简单的Treiber Stack来保存等待线程。

源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FutureTask<V> implements RunnableFuture<V> {  


private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
// 1
private Callable<V> callable;
// 2
private Object outcome;
// 3
private volatile Thread runner;
// 4
private volatile WaitNode waiters;

内部状态可能得迁转过程

  • NEW -> COMPLETING -> NORMAL //正常完成
  • NEW -> COMPLETING -> EXCEPTIONAL //发生异常
  • NEW -> CANCELLED //取消
  • NEW -> INTERRUPTING -> INTERRUPTED //中断

标注代码分析

  1. 内部的callable,运行完成后设置为null。
  2. 如果正常完成,就是执行结果,通过get方法获取;如果发生异常,就是具体的异常对象,通过get方法抛出。本身没有volatile修饰, 依赖state的读写来保证可见性。
  3. 执行内部callable的线程。
  4. 存放等待线程的Treiber Stack。

JDK1.7 Condition

介绍

Condition是一个多线程间协调通信的工具类。使得某个或者某些线程一起等待某个条件(Condition),只有当该条件具备(signal()或者signalAll()被带调用)时,这些等待线程才会被唤醒,从而重新争夺锁。
ConditionObject监视器方法(wait()notify()notifyAll())分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set()wait-set)。其中,Lock替代synchronized和语句的使用,Condition替代Object监视器方法的使用。
Lock对应Synchronized,使用之前都要先获取锁。

Object Condition
休眠 wait await
唤醒个线程 notify signal
唤醒所有线程 notifyAll signalAll

Condition它更强大的地方在于。能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,就是多个监视器的意思。在不同的情况下使用不同的Condition

Tomcat(七)设计模式总结

前言

分析一下Tomcat中所涉及到设计模式,本文我们将主要来分析外观模式,观察者模式,责任链模式,模板方法模式,命令模式。
在开始本文之前,先说明一下对于设计模式的一点看法。曾经经常看到网上有人讨论设计模式,也偶尔会遇到有人非要严格按照GOF设计模式的类图以及其中的角色去套用别人的设计,只要类图不一样,或者角色多了或者少了就会觉得怎么和官方定义的模式不一样,我个人认为这是对设计模式的误解。设计模式其实不仅仅存在软件行业,各行各业其实都有模式,它是所在行业对一些通用问题解决方案的总结和抽象,是一种对常见问题的抽象的解决方案,不是一种具体的实现,所以我觉得在讨论设计模式的时候,千万别一个劲的套用GOF设计模式中的类图以及其中所涉及到的角色,而是要理解设计模式的思维,理解设计模式的使用场景,在代码层面上有无数种实现,无需照搬(或者copy)代码。

外观模式

在Tomcat中对于RequestResponseStandardSessionApplicationContextStandardWrapper都采用外观模式,它的类图如下。

通过上图,可以看到RequestFacade包装了Request,它们都实现了HttpServletRequest,当传递Request对象给应用的时候,其实是返回了RequestFacade对象,而RequestFacade内部可以根据是否自定义了安全管理器来进行相应的操作。
对于ResponseStandardSession等处理是类似的,这里就不写了。

Tomcat(六)Session管理机制

Tomcat session相关的类图

通过上图,可以看出每一个StandardContext会关联一个Manager,默认情况下Manager的实现类是StandardManager,而StandardManager内部会聚合多个Session,其中StandardSession是Session的默认实现类,当我们调用Request#getSession()的时候,Tomcat通过StandardSessionFacade这个外观类将StandardSession包装以后返回。
org.apache.catalina.connector.Request#getSession()

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Return the session associated with this Request, creating one
* if necessary.
*/
@Override
public HttpSession getSession() {
Session session = doGetSession(true);
if (session == null) {
return null;
}

return session.getSession();
}

从上面的代码,可以看出首先首先调用doGetSession()获取Session,然后再调用Session#getSession()返回HttpSession,那接下来再来看看doGetSession()

Tomcat(五)类加载器机制

当一个JVM启动时,至少有三个ClassLoader会被启动:

  • Bootstrap 类加载器:加载Java核心包,它们被置放在(<JAVA_HOME>/lib目录下,这部分是JVM的一部分,往往使用native code完成。
  • Extensions类加载器:加载Java扩展包,即位于<JAVA_HOME>/lib/ext下的包,这里要注意的是,有些JVM的这个加载器和Bootstrap类加载器是同一个加载器,而Sun是把二者分开的,其实现类为sun.misc.Launcher$ExtClassLoader
  • System类加载器:这个类加载器加载CLASSPATH下的类,Sun的默认实现是sun.misc.Launcher$ExtClassLoader

这三者之间的父子关系是:Bootstrap类加载器是Extensions类加载器的父类加载器,而Extensions类加载器是System类加载器的父类加载器。
Tomcat的Classloader机制,从Bootstrap开始。在BootStrap初始化的时候,调用org.apache.catalina.startup.Bootstrap#initClassLoaders(),这个方法里面创建3个ClassLoader,它们分别是commonLoader,catalinaLoader,sharedLoader,其中catalinaLoader,sharedLoader的父亲加载器是commonLoader,initClassLoaders执行的过程中会执行createClassLoader,而createClassLoader是根据conf/catalina.properties文件中common.loaderserver.loadershared.loader的值来初始化,它的代码如下。
org.apache.catalina.startup.Bootstrap#createClassLoader()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {

String value = CatalinaProperties.getProperty(name + ".loader");
// 1
if ((value == null) || (value.equals("")))
return parent;
// 2
value = replace(value);

List<Repository> repositories = new ArrayList<Repository>();

StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken().trim();
if (repository.length() == 0) {
continue;
}

// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(
new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
}

// Local repository
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositories.add(
new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(
new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(
new Repository(repository, RepositoryType.DIR));
}
}
// 3
ClassLoader classLoader = ClassLoaderFactory.createClassLoader
(repositories, parent);

// Retrieving MBean server
MBeanServer mBeanServer = null;
if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
} else {
mBeanServer = ManagementFactory.getPlatformMBeanServer();
}

// Register the server classloader
ObjectName objectName =
new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
mBeanServer.registerMBean(classLoader, objectName);

return classLoader;

}

  • 标注1的代码:判断如果catalina.properties中没有配置对应的loader属性的话,直接返回父加载器,而默认情况下,server.loadershared.loader为空,那么此时的catalinaLoader,sharedLoader其实是同一个ClassLoader。
  • 标注2的代码:根据环境变量的配置替换字符串中的值,默认情况下,common.loader的值为common.loader=${catalina.base}/lib${catalina.base}/lib/.jar${catalina.home}/lib${catalina.home}/lib/.jar,这里会将catalina.basecatalina.home用环境变量的值替换。
  • 标注3的代码:最终调用org.apache.catalina.startup.ClassLoaderFactory#createClassLoader()静态工厂方法创建URLClassloader的实例,而具体的URL其实就是*.loader属性配置的内容,此外如果parent = null的话,则直接用系统类加载器。

Tomcat(四)请求处理流程

前言


在开始本文之前,先来看看一个Http请求处理的过程,一般情况下是

1
2
3
4
5
浏览器发送http请求
->建立Socket连接
->通过Socket读取数据
->根据http协议解析数据
->调用后台服务完成响应

详细的流程图如上图所示。Tomcat既是一个HttpServer也是一个Servlet 容器,那么这里必然也涉及到如上过程,首先根据HTTP协议规范解析请求数据,然后将请求转发给Servlet进行处理,因此顺应这样的思路,本文也将从Http协议请求解析,请求如何转发给Servlet两个方面来进行分析。首先来看Http协议请求解析。

Tomcat(三)关闭过程

源码分析

已经知道Tomcat启动以后,会启动6条线程,它们分别如下
Tomcat threads

1
2
3
4
5
6
"ajp-bio-8009-AsyncTimeout" daemon prio=5 tid=7f8738afe000 nid=0x115ad6000 waiting on condition [115ad5000]
"ajp-bio-8009-Acceptor-0" daemon prio=5 tid=7f8738b05800 nid=0x1159d3000 runnable [1159d2000]
"http-bio-8080-AsyncTimeout" daemon prio=5 tid=7f8735acb800 nid=0x1158d0000 waiting on condition [1158cf000]
"http-bio-8080-Acceptor-0" daemon prio=5 tid=7f8735acd000 nid=0x1157cd000 runnable [1157cc000]
"ContainerBackgroundProcessor[StandardEngine[Catalina]]" daemon prio=5 tid=7f8732850800 nid=0x111203000 waiting on condition [111202000]
"main" prio=5 tid=7f8735000800 nid=0x10843e000 runnable [10843c000]

其中5条是Dameon线程,而对于Java程序来说,当所有非Dameon程序都终止的时候,JVM就会退出,因此要想终止Tomcat就只需要将main()这一条非Dameon线程终止即可。
Dameon线程又叫后台或者守护线程,它负责在程序运行期提供一种通用服务的线程,比如垃圾收集线程,非Dameon线程和Dameon线程的区别就在于当程序中所有的非Daemon线程都终止的时候,JVM会杀死余下的Dameon线程,然后退出。
接下来,一步步的分析如何来让main()线程终止,要想终止它,还是得从Tomcat的启动中来寻找答案,在分析Tomcat容器启动的时候,在Catalina#start()中有一段代码,接下来就来看看这段代码。

Tomcat(二)启动过程

Tomcat组件生命周期管理

Tomcat中ServerServiceConnectorEngineHostContext的继承关系图,你会发现它们都实现org.apache.catalina.Lifecycle接口,而org.apache.catalina.util.LifecycleBase采用模板方法模式来对所有支持生命周期管理的组件的生命周期各个阶段进行总体管理,每个需要生命周期管理的组件只需要继承这个基类,然后覆盖对应的钩子方法即可完成相应的生命周期阶段性的管理工作。
下面我们首先来看看org.apache.catalina.Lifecycle接口的定义,它的类图如下图所示。

从上图我们可以清楚的看到LifeCycle中主要有四个生命周期阶段,它们分别是init()初始化start()启动stop()停止destory()销毁。知道这四个生命周期阶段以后,看看org.apache.catalina.util.LifecycleBase是如何实现模板方法模式的。
那接下来我们就来看看org.apache.catalina.util.LifecycleBase类的定义,它的类图如下所示。

上图中用红色标注的四个方法就是模板方法模式中的钩子方法,子类可以通过实现钩子方法来纳入到基类已经流程化好的生命周期管理中。
上面我们对LifeCycleLifeCycleBase有一个总体的认识,接下来,我们通过查看org.apache.catalina.util.LifecycleBase的源代码来具体的分析一下。

Tomcat(一)总体结构

前言

本章以Tomcat7为主。

Tomcat的总体结构

Tomcat即是一个Http服务器也是一个Servlet容器,它的总体结构我们可以用下图来描述。

通过上图我们可以看出Tomcat中主要涉及ServerServiceEngineConnectorHostContext组件。在Tomcat二进制分发包解压后,在conf目录中有一个server.xml文件,server.xml文件中已经包含上述的几个名称。Tomcat7.0.42分发包中的server.xml来具体分析一下,它的内容如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?xml version='1.0' encoding='utf-8'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="8005" shutdown="SHUTDOWN">
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
<Listener className="org.apache.catalina.core.JasperListener" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>

<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">

<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->


<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
<!-- Define a SSL HTTP/1.1 Connector on port 8443
This connector uses the JSSE configuration, when using APR, the
connector should be using the OpenSSL style configuration
described in the APR documentation -->
<!--
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
-->

<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />


<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->

<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine name="Catalina" defaultHost="localhost">

<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->

<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>

<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">

<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->

<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />

</Host>
</Engine>
</Service>
</Server>

Server

Server是Tomcat中最顶层的组件,它可以包含多个Service组件。在Tomcat源代码中Server组件对应源码中的org.apache.catalina.core.StandardServer类。
StandardServer的继承关系图如下图所示。

JDK1.6 ThreadPoolExecutor

介绍

ThreadPoolExecutor是JUC包中提供的线程池,使用ThreadPoolExecutor的好处一方面是能重用线程资源,避免重复创建线程带来的开销;另一方面是ThreadPoolExecutor提供了内部资源(线程、任务)的管理功能,方便我们监控线程池工作状态。

源码分析

ThreadPoolExecutor类结构图

Executor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Executor {

/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the <tt>Executor</tt> implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution.
* @throws NullPointerException if command is null
*/
// 1
void execute(Runnable command);
}

标注代码分析

  1. 根据注释很好理解。command可以在新线程里执行,可以在线程池里执行,可以被调用线程执行等等。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×