`
ftj20003
  • 浏览: 132347 次
  • 性别: Icon_minigender_1
  • 来自: ...
社区版块
存档分类
最新评论

多线程基础总结九--Mina窥探(1)

    博客分类:
  • Java
阅读更多
    一直以来的多线程的基础总结都是脱离应用的,但是要说多线程的应用就不能不说Mina。Apache Mina作为一个高性能的Java异步并发网络通讯框架,其内部的多线程的设计和实现可谓是学习多线程的良药。手上的Mina源码是svn剪下来的最新的代码,mvn转化成eclipse项目后导入mina-core的源码看看多线程的应用吧。

    首先简单的介绍在org.apache.mina.core.service包里的核心接口之一:IoService。这个接口是对于服务器端接收连接和客户端发起连接这两种服务的顶层抽象,所以就有了IoAcceptor和IoConnector两个子接口的继承与隔离。很拍马屁的说,从这个小细节可以看到mina架构的精细。这种程度的接口的隔离最重要的就是对接口内抽象行为的准确划分,错误的划分接口的职责将使得后面得实现显得不合理甚至是错误。只是不知道负责抽象的设计人员是否是一次性的抽象成功,如果是只能说牛x。至于交互会话IoSession和实际的I/O操作处理器IoProcessor以及底层处理I/O事件的IoHandle这些接口就不废话了,今天看的是与IoServiceListener有关的多线程应用。IoServiceListener主要是用来监听IoService相关的事件,而今日主角--IoServiceListenerSupport则是用来把IoService和对应的IoServiceListener包装在一起进行管理的辅助类。先看看其源码:
public class IoServiceListenerSupport {
    /** The {@link IoService} that this instance manages. */
    private final IoService service;

    /** A list of {@link IoServiceListener}s. */
    private final List<IoServiceListener> listeners = new CopyOnWriteArrayList<IoServiceListener>();

    /** Tracks managed sessions. */
    private final ConcurrentMap<Long, IoSession> managedSessions = new ConcurrentHashMap<Long, IoSession>();

    /**  Read only version of {@link #managedSessions}. */
    private final Map<Long, IoSession> readOnlyManagedSessions = Collections.unmodifiableMap(managedSessions);

    private final AtomicBoolean activated = new AtomicBoolean();
    
    /** Time this listenerSupport has been activated */
    private volatile long activationTime;
    
    /** A counter used to store the maximum sessions we managed since the listenerSupport has been activated */
    private volatile int largestManagedSessionCount = 0;
    
    /** A global counter to count the number of sessions managed since the start */
    private volatile long cumulativeManagedSessionCount = 0;

    /**
     * Adds a new listener.
     * 
     * @param listener The added listener
     */
    public void add(IoServiceListener listener) {
        if (listener != null) {
            listeners.add(listener);
        }
    }

    /**
     * @return true if the instance is active
     */
    public boolean isActive() {
        return activated.get();
    }

    /**
     * Calls {@link IoServiceListener#serviceActivated(IoService)}
     * for all registered listeners.
     */
    public void fireServiceActivated() {
        if (!activated.compareAndSet(false, true)) {
            // The instance is already active
            return;
        }

        activationTime = System.currentTimeMillis();

        // Activate all the listeners now
        for (IoServiceListener listener : listeners) {
            try {
                listener.serviceActivated(service);
            } catch (Throwable e) {
                ExceptionMonitor.getInstance().exceptionCaught(e);
            }
        }
    }

    /**
     * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
     * 
     * @param session The session which has been created
     */
    public void fireSessionCreated(IoSession session) {
        boolean firstSession = false;
        
        if (session.getService() instanceof IoConnector) {
            synchronized (managedSessions) {
                firstSession = managedSessions.isEmpty();
            }
        }
    ...
        
        cumulativeManagedSessionCount ++;
    ...
    }
}

这里为了说明多线程的应用,我仅仅节选相关的方法和方法片段。从这个类中我们还是能看到丰富的多线程的应用的。下面就开始这盘菜吧!

     首先从全局变量开始看看:CopyOnWriteArrayList,ConcurrentMap,AtomicBoolean,volatile堪称多线程小动员了。后两者之前都有介绍暂时省略,后面的方法分析时会看到使用情景。前两者都是java.util.concurrent包下的并发集合框架成员。ConcurrentMap是高效的并发Map实现,主要是采取分段加锁的机制,默认是16段锁,所以多线程的竞争大大的降低。只有key的hash分布在同一段位上的线程之间存在竞争。

     而CopyOnWriteArrayList是ArrayList的一个线程安全的变体,变态之处在于对其的所有的可变操作都是对底层的数组的进行的一次新的复制。看看其实现原来是使用ReentrantLock来保证可变时的线程安全,又多了一个多线程的成员啊。当然使用这个并发集合实现是需要特殊的情况的(要知道它的每次的可变都是全盘复制,这意味着很大的性能成本):如果遍历集合的操作次数大大的超过了可变操作时,这时候它的性能优势就体现出来了。因为内部的数组使用的是volatile的,所以遍历查找时都不用同步而能保证可见性,这是锁同步无法比拟的。要说mina重视性能从这儿可见一斑,毕竟每个全局变量都是为了性能去选择相应的同步机制,而不是synchronized通吃天下。

     IoServiceListenerSupport的fireServiceActivated(),fireServiceDeactivated(),fireSessionCreated(),fireSessionDestroyed()方法均有遍历listeners的操作,如果这些方法在线程之间频繁的使用的话,那么无疑使用CopyOnWriteArrayList是个很好的解决方案。managedSessions主要是管理IoSession的,需要使用并发Map的数据结构,那么ConcurrentMap无疑已被证明是相对出色的并发Map。activated类似一个开关的设计,看看为什么使用无锁得AtomicBoolean?
fireServiceActivated() {
        if (!activated.compareAndSet(false, true)) {
            // The instance is already active
            return;
        }
        ...
}

这里可以看到activated的状态改变是要依赖其原来的值,也就是如果使用volatile的话,要判断之前的是否为false,满足则置为true。这样的操作对于volatile只能使用锁同步实现。而AtomicBoolean的“CAS”轻松的使用无锁同步原语解决了这个问题。compareAndSet(...)不管有多少个线程执行,只有取得activated最新值得线程才能返回true,其余的都会是false。这就看到mina的committer还是很纠结性能的。

      然后看看那几个volatile的变量吧。首先是activationTime,这个变量除了有自身的get()可以随时取得最新的值之外,就在fireServiceActivated()内出现了:activationTime = System.currentTimeMillis();可以看到仅仅是一次性的赋值而且不依赖其自身的值。所以完全的满足线程安全的条件。largestManagedSessionCount的使用和其类似。

      再来看看cumulativeManagedSessionCount,它是一个全局的计数器,负责记录启动后所管理得会话数量。除了同样有get()方法之外就是出现在fireSessionCreated(...)方法中:cumulativeManagedSessionCount ++;这引起了我的注意,因为这样的写法是不能保证线程同步的!因为volatile的变量根本无法完成“++”的原子操作。“++”是需要依赖其自身的值而进行更改的操作。为此我简单的写了个验证的例子证明了这个铁律。
/**
 *
 * @author: yanxuxin
 * @date: 2010-1-16
 */
public class VolatileTest {

	public static void main(String[] args) {
		final VolatileSample sample = new VolatileSample();
		
		Runnable runnable = new Runnable() {
			public void run() {
				for(int i = 0; i < 500; i++) {
					System.out.println(sample.incrementAndGet());
				}
			}
		};
		
		for(int i = 0; i < 50; i++) {
			new Thread(runnable).start();
		}
	}
}

class VolatileSample {
	private volatile long counter = 0;
	
	public long incrementAndGet() {
		counter++;
		return get();
	}
	
	public long get() {
		return counter;
	}
}

使用50个线程每个线程执行500次对counter进行叠加。正确的答案应该是25000,但是很不幸的是仅仅测试几次就出现了24999和其他的小于25000的最大值。所以很遗憾的发现mina的这个变量的方案的选择是有问题的!

       这里应该是使用的是AtomicLong,而对应的fireServiceActivated(...)内的代码如果不使用API提供的话应该是:
    for (;;) {
         long current = cumulativeManagedSessionCount.get();
         long next = current + 1;
         if (compareAndSet(current, next))
             break;
    }

虽然是for循环+CAS的atomic杀手锏,但是不要怕,一般for循环cpu都是一次搞定,极少情况是多次,性能不会有什么明显影响。这个bug可能在数量较少的线程的情况下很难显现,或者是mina的开发者考虑使用情景故意的宽松线程机制?从严谨性来看是我个人认为是不对的。

       最后就是看看一个有趣的synchronized的代码片段:
    /**
     * Close all the sessions
     * TODO disconnectSessions.
     *
     */
    private void disconnectSessions() {
        Object lock = new Object();
        ...
        try {
            synchronized (lock) {
                while (!managedSessions.isEmpty()) {
                    lock.wait(500);
                }
            }
        } catch (InterruptedException ie) {
            // Ignored
        }
    }

这个方法是关闭所有的会话,但是为了减少线程锁的竞争使用了一个很可爱的方式。这里的lock是个局部变量,所以每个进入这个方法的线程都会拥有一个lock对象,而synchronized(lock)是迷惑的重点。这个synchronized不会使得进入方法的线程们产生任何的竞争,因为每个线程都能获得属于自己的lock。synchronized的作用就在于lock.wait(500)的调用,就因为wait方法必须要synchronized的配合,所以就出现了这个可爱的代码。这段代码的主要意图就是不管几个线程去关闭所有会话,每个线程都是间隔500ms去检查managedSessions是否为空。这里没有对managedSessions使用synchronized的原因就是为了减少线程锁对managedSessions的资源独占,而改用while循环的机制宁愿等待也不为了检查managedSessions而影响其他线程的工作。很精巧的一个实现!

       写到这里,算是这个小菜的结尾了,总感觉意犹未尽。mina对高性能并发的目标还是从代码上得到了一些的体现。当然它的高性能主要是对NIO的封装使用,不过这一切都是建立在多线程的并发基础上的。至于之前无意发现的bug(个人认为)如果mina的committer也有相同的认知的话,我想以后会有相应的修改的。
0
0
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    深入理解Apache_Mina_(5)----_配置Mina的线程模型[归类].pdf

    总结来说,Apache Mina的线程模型设计允许开发者灵活地调整资源分配,以适应不同场景的需求。通过理解和配置这些线程模型,可以有效地优化Mina应用程序的性能和并发能力,确保网络服务的高效运行。

    mina2.0 含11个jar包

    mina-core-2.0.0-M6.jar mina-example-2.0.0-M6.jar mina-filter-codec-netty-2.0.0-M6.jar mina-filter-compression-2.0.0-M6.jar mina-integration-beans-2.0.0-M6.jar mina-integration-jmx-2.0.0-M6.jar mina-...

    Spring-mybaits-mina整合,可监听多端口

    1:Spring-mybaits-mina三大框架的整合,mina可以使用Spring的数据库数据库连接池,无需额外定义mybaits连接池。 2:mina可以自定义监听一个或者多个端口,可以从数据库中读取

    深入理解Apache_Mina_(1)----_Mina的几个类

    ### 深入理解 Apache Mina (1) —— Mina 的几个核心类 #### 一、Mina 核心类概述 Mina (Multi-threaded Integrated Network Architecture) 是一个高性能且灵活的网络应用程序框架,它支持多种传输协议,如 TCP、...

    apache ------Mina

    apache MinaMina apache MinaMina apache MinaMina apache MinaMinaapache MinaMina apache MinaMina apache MinaMina apache MinaMina apache MinaMina

    mina2.0全部jar包

    mina-transport-apr-2.0.7.jar是MINA对Apache Portable Runtime (APR)的支持,APR是一个库,提供了操作系统级别的接口,如socket和线程,可以提升MINA在某些平台上的性能。 mina-integration-jmx-2.0.7.jar提供了...

    mina多线程

    根据给定的信息,我们可以从标题“mina多线程”、描述“mina java多线程开发框架 NIO”以及部分给出的内容中提炼出一系列与Mina框架相关的知识点。 ### Mina框架简介 Mina(Multipurpose Infrastructure Networked...

    Spring-mybaits-mina三大框架的整合

    在"spring-mina-multi-port"这个文件名中,我们可以推测这是一个示例项目,展示了如何在Spring中配置Mina监听多个端口。这可能涉及到在Spring配置文件中创建多个IoAcceptor实例,每个配置不同的端口号,从而实现服务...

    mina-master_mina_

    1. **源代码**:MINA服务端的Java源代码,通常会包括一个或多个`Handler`类,这些类是MINA的核心部分,处理网络事件和数据传输。例如,`EchoHandler`可能会实现简单的回显服务,接收客户端发送的数据并原样返回。 2...

    MINA 2.0.0-M6

    压缩包中的“mina-2.0.0-M6”文件可能包含以下内容: 1. `lib`目录:包含MINA库的JAR文件和其他依赖库。 2. `docs`目录:文档,包括API参考、用户指南和开发者文档。 3. `src`目录:源代码,供开发者参考和学习MINA...

    关于apache Mina Server

    深入理解Apache_Mina_(1)----_Mina的几个类 深入理解Apache_Mina_(2)----_与IoFilter相关的几个类 深入理解Apache_Mina_(3)----_与IoHandler相关的几个类 深入理解Apache_Mina_(4)----_IoFilter和IoHandler的区别和...

    mina-http-2.0.7.jar.zip_mina_mina 获取POST_mina-http_mina-http-2.0

    1. MINA框架基础: MINA框架提供了一种事件驱动的非阻塞I/O模型,这种模型允许服务器处理大量的并发连接,同时保持较低的内存占用。它通过使用NIO(Non-blocking Input/Output)技术,使得服务器在等待数据到来时...

    apache-mina-2.0.4.rar_apache mina_mina

    这个"apache-mina-2.0.4.rar"压缩包包含的是Apache Mina 2.0.4版本的源代码,是深入理解和定制Mina的理想资源。 Apache Mina的核心理念是提供一个简洁的API,使得开发者能够专注于业务逻辑,而不是网络通信的底层...

    mina-core-2.0.0-RC1.jar,mina-filter-compression-2.0.0-RC1.jar

    在这个场景中,我们关注的是MINA的核心组件以及两个特定的过滤器和传输组件:`mina-core-2.0.0-RC1.jar`、`mina-filter-compression-2.0.0-RC1.jar`和`mina-transport-apr-2.0.0-RC1.jar`。 **MINA Core (mina-core...

    EJB-JSF-JPA-MINA

    它的异步事件驱动模型使得MINA在处理大量并发连接时表现优秀,适用于需要高吞吐量和低延迟的应用场景。 这些技术在企业级应用开发中都有其特定的角色。EJB处理核心业务逻辑,JSF负责用户界面,JPA处理数据持久化,...

    Android-基于ApacheMINA进行封装实现AndroidAPP作为服务器客户端进行通讯

    - **线程管理**:MINA默认使用多线程处理连接,但在Android中需要谨慎使用线程,以免影响UI响应。 - **内存优化**:考虑到Android设备的内存限制,应合理控制缓存大小,避免内存泄漏。 4. **cuihp-AndroidServer-...

    Cindy 3.0b1 的源码--- Mina的兄弟

    Netty、Mina、Cindy都是不错的NIO开源框架,后两者都是在Netty的基础上演化出来的。所以要学习好Java的异步通信框架,这三个都是不可多得的好材料。 本资源仅供学习和参考使用,若要进行开发,请下载相应的Jar包

    MINA-2.0.0-M3

    综上所述,MINA-2.0.0-M3源码的学习将涵盖Java网络编程、非阻塞I/O、事件驱动编程、协议处理等多方面知识,对于想要深入了解网络应用开发和优化的Java开发者来说,是一份宝贵的资料。通过研究MINA的源代码,不仅可以...

    apache-mina-2.0.16-jar包-代码.zip

    标题中的"apache-mina-2.0.16-jar包-代码.zip"表明这是一个包含Apache Mina 2.0.16版本源代码的JAR文件压缩包。这个压缩包通常用于开发者进行源码级别的学习、调试或定制工作。通过解压这个文件,你可以获得Mina库的...

Global site tag (gtag.js) - Google Analytics